在云笔记项目中,补充了部分反射的知识,反射这一部分基础知识非常重要,前面学习的框架Spring和MyBatis读取xml配置文件创建对象,以及JDBC加载驱动等都用了反射,但只知道有这个东西,具体不知道怎么用,大概的原理是怎么样的,现在简单的记录下

什么是反射

反射(Reflection)是Java提供的动态执行机制,可以动态加载类,动态创建对象,动态获取类信息,比如接口信息,方法信息,属性信息,构造信息等,是JDK1.4开始出现的功能。并可以通过获取到的信息动态创建对象,动态调用方法等。如果想更好的感受反射,可以从静态执行方式和动态执行方式两种去对比。

a.静态执行:Java代码通过编译以后就确定的执行次序,称为静态执行次序。比如新建一个对象Foo foo=new Foo(),然后调用对象的方法foo.test()执行,这就是静态执行,代码在编译期就知道具体的对象是什么,对象要执行的方法是什么。

b.动态执行:在运行期间才确定要创建哪个类,执行类的哪个方法。也就是编译期无法得知具体的类信息,也无法得到类中的方法信息,等具体加载类执行时才能知道。Java反射API,可以实现动态执行加载类和执行方法的功能。

反射的核心是 JVM 在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。

为了更好理解反射,先准备两个写好的类,后面做用素材测试使用:

Foo类:

 package Test;

 public class Foo {
//加几个属性,演示通过反射得到所有的属性
public String name;
public int age;
private int salary; //加一个构造器,演示通过反射获得构造器
// public Foo(String name) {
// super();
// this.name = name;
// } //加几个方法,演示通过反射动态调用方法
private String getPrice() {
return "100";
} public String hello() {
return "hello reflect";
}
}

Too类:

 package Test;

 public class Too {
public String name;
public int age;
private int salary; public String Hello() {
System.out.println("Hello Too");
return "Success";
}
}

反射功能

a.动态加载类,主要有三种方法:

(1)使用Class类的forName()静态方法

作用是将类名对应的类加载到方法区,如果类名错误就抛出异常。

forName()工作原理:写好一个类,比如Foo.java类,通过编译后会生成Foo.class字节码文件,当执行Class cls=Class.forName("Foo")后,Class.forName()这个方法会首先读取Foo.class字节码文件,将其加载到方法区中。Class.forName()执行完后的返回值就是一个具体的Class类型对象,储存在堆中,其通向方法区。而cls是一个指向具体对象的引用,会储存在栈中。通过cls,可以获取Foo类的一切信息,属性和方法等。当forName()方法中的类名更改后,其加载新的类对应的字节码文件到方法区,从而实现了类的动态加载。

 package Test;

 import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Scanner; public class Demo { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
//动态加载类
Scanner scan=new Scanner(System.in);
//得到用户输入的类名
System.out.println("请输入类名:");
String className=scan.nextLine();
//动态加载类
Class cls=Class.forName(className);
System.out.println("---------------类信息---------------");
System.out.println(cls);//输出class 包名.类名
System.out.println(cls.getName());//输出包名.类名
}
}

控制台输入Test.Foo后,输出结果如下,getName()获得的是Java内部使用的真正名称,包含包名和类名,其他还有getSimpleName()和getPackage()方法等,分别代表返回不包含包名和只包含包名的信息:

(2)直接获取某一个对象的class

 Class<Date> cls = Date.class;

获取Class对象如果提前知道了类名,不一定需要实例对象,可以使用<类名>.class获取Class对象。

(3)调用某个对象的getClass()方法

 StringBuilder str = new StringBuilder("123");
Class<?> cls = str.getClass();

所有类的根父类Object有一个方法 public final native Class<?> getClass() ,可以获取对象的Class对象。Class是一个泛型类,使用getClass()方法时并不知道返回的具体类是什么类型,因此返回Class<?>。问号"?"代表类型的实参,不是类型形参,其代表所有类型的父类,是一种实际的参数。

b.动态创建对象,主要有两种方法

(1)可以使用cls.newInstance()方法创建对象,相当如使用无参构造器创建了对象。

Object obj=cls.newInstance()

特点:动态创建对象;可以创建任何对象;cls对应的类必须有无参数构造器,如果没有将抛出异常,(一般符合javabean规范的类都有无参数构造器)

(2)使用cls.getConstructor()方法得到Constructor对象,然后使用Constructor.getInstance()方法获取对象,这个构造器可以传入参数,是跟第一种主要的区别。

在上述main方法中加上如下代码获取对象,注释的部分是通过带参数的构造器创建对象:

         //动态创建对象
//1 使用cls.newInstance()来获取对象,使用无参数构造器
Object obj=cls.newInstance();
System.out.println("---------------对象信息---------------");
System.out.println(obj);
//2 使用cons.newInstance()获取对象,使用特定的构造器
// Constructor cons=cls.getConstructor(String.class);
// Object obj=cons.newInstance("clyang");
// System.out.println(obj);

同样控制台使用Test.Foo类进行测试,输出结果为:

c.反射可以查找类中的方法

可以返回类中声明的全部方法信息,eclipse开发工具中,当得到一个对象后,可以通过输入点,就可以列出对象里所有的方法,其实也是用的反射机制。如Foo foo=new Foo(),当输入foo.时,"foo."后面会列出一堆方法和属性信息,其实就是在点了后,java获得了foo,然后获取到了类名,通过java反射得到这个类下的方法和属性,然后将其输出到界面,列出来展示给开发人员。

(1)getDeclaredMethods()

在上述main方法中加上如下代码获取所有声明的方法,使用getDeclaredMethods()方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法:

         //动态检查类中声明的方法信息
Method[] method=cls.getDeclaredMethods();//返回所有private,public,protected等修饰的方法
System.out.println("---------------方法信息---------------");
for(Method m:method) {
System.out.println(m);//输出方法信息
}

同样使用Test.Foo类进行测试,控制台输出结果为:

(2)getMethods()

方法返回某个类的所有公用(public)方法,包括其继承类的公用方法.

(3)getMethod(String name,Class<?>... parameterTypes)

方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象

d.动态执行方法

(1) 需要在动态创建对象后再动态执行方法

Object obj=cls.newInstance();//动态创建对象

(2)找到对象对应的类型方法信息,方法信息在类上查找

method m=cls.getDeclaredMethod(name);//找到对象对应类cls中的方法信息

(3)接下来就可以调用方法了

m.invoke(obj);//字面意思是,m方法调用了obj,个人猜测是m方法唤醒了obj,然后底层调用了obj.m()方法。

         //动态调用方法
System.out.println("请输入方法名");
String methodName=scan.nextLine();
Method m=cls.getDeclaredMethod(methodName);
//如果想让private方法也能被调用,需要加上
System.out.println("---------------动态执行方法---------------");
m.setAccessible(true);
Object o=m.invoke(obj);
System.out.println(o);

同样使用Test.Foo类进行测试,测试执行hello方法,控制台输出结果为:

e.反射也可以查找类中的属性

(1)getDeclaredFields()

可以返回类所有已声明的成员变量,但不能得到其父类的成员变量,Field[] field=cls.getDeclaredField()。

        //返回类的所有成员变量
Field[] fields=cls.getDeclaredFields();
System.out.println("---------------属性信息---------------");
for(Field f:fields) {
System.out.println(f);
}

同样使用Test.Foo类进行测试,控制台输出结果可以看出私有的成员变量也可以得到:

(2)getField

访问共有的成员变量。

f.反射可以获取类中的构造器

可以获取类中所有声明的构造器,Constructor[] constructor=cls.getDeclaredConstructors();

        //返回类中声明的构造器
Constructor[] constructor=cls.getDeclaredConstructors();
System.out.println("---------------构造器信息---------------");
for(Constructor c:constructor) {
System.out.println(c);
}

同样控制台使用Test.Foo类进行测试,输出结果为:

发现输出的是默认构造器方法,因为在Foo类中,没有写构造器,因此创建对象时默认调用了无参数构造器。

g.反射的用途

(1)Eclipse快捷菜单使用了反射,利用反射发现类的属性和方法

(2)Spring利用了反射:动态加载类,动态创建bean,动态注入属性,包括私有属性注入,动态解析注解

(3)Mybatis利用了反射,查询时候,动态将查询结果利用反射注入到bean并返回

(4)Junit使用了反射

(5)注解的解析使用了反射

(6)Servlet调用使用了反射

总结

反射内容非常复杂,现在只是学习如何基本的使用,具体底层的实现,invoke方法的原理,还需要后续学习补充。

参考博客:https://www.sczyh30.com/posts/Java/java-reflection-1/#%E4%B8%80%E3%80%81%E5%9B%9E%E9%A1%BE%EF%BC%9A%E4%BB%80%E4%B9%88%E6%98%AF%E5%8F%8D%E5%B0%84%EF%BC%9F

参考博文:https://www.cnblogs.com/coprince/p/8603492.html

参考书籍:《Java编程的逻辑》

云笔记项目-Java反射知识学习的更多相关文章

  1. JAVA反射机制—学习总结

    最近收到很多关于Java反射机制的问题留言,其实Java反射机制技术方面没有太多难点,或许是大家在学习过程中遗漏了细小知识点,导致一些问题无法彻底理解,现在我们简单的总结一下,加深印象.什么是反射机制 ...

  2. Java底层知识学习:Bytecode and JMM

    最近在跟着耗子哥的程序员练级指南学习Java底层知识,结合<深入理解Java虚拟机>这本书在看,写笔记,看资料,成长中…… 目前看完了第二章JMM和各内存区OOM的情况 一篇图文并茂介绍字 ...

  3. java反射知识相关的文章

    整理的反射相关的文章: (1).通俗理解反射(知乎):学习java应该如何理解反射? (2).关于反射比较深入的博文地址:深入解析Java反射(1) - 基础 贴出我反射调用代码:(craw,dept ...

  4. Java反射机制——学习总结

    前几天上REST课,因为涉及到Java的反射机制,之前看过一直没有用过,有些遗忘了,周末找了些资料来重新学习,现在总结一下,加深印象. 什么是反射机制? 参考百度百科对java反射机制的定义: “JA ...

  5. Java基础知识学习(九)

    GUI开发 先前用Java编写GUI程序,是使用抽象窗口工具包AWT(Abstract Window Toolkit).现在多用Swing.Swing可以看作是AWT的改良版,而不是代替AWT,是对A ...

  6. Java反射机制学习与研究

    Java反射机制:可以获取正在运行时的Java对象. 1.判断运行时对象对象所属的类. 2.判断运行时对象所具有的成员变量和方法. 3.还可以调用到private方法,改变private变量的值. S ...

  7. Java 反射机制学习资料

    Java反射——引言 Java反射——Class对象 Java反射——构造函数 Java反射——字段 Java反射——方法 Java反射——Getter和Setter Java反射——私有字段和私有方 ...

  8. java反射知识

    java反射机制是在运行状态中,对于任意一个类(class文件),都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称 ...

  9. Java反射机制学习

    Java 反射是Java语言的一个很重要的特征,它使得Java具体了“动态性”. 在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答 ...

随机推荐

  1. spring-task解决定时问题

    *  spring3以上版本,spring-content自带 spring-task ,来解决工程中的定时问题  基于注解配置spring定时任务 spring配置文件如下: <?xml ve ...

  2. oracle 简单crud

    -- 商品表 -- CREATE TABLE "SCOTT"."GOODS" ( "id" NUMBER NOT NULL, "n ...

  3. 使用maven profile指定配置文件打包适用多环境

    新建maven项目,   在pom.xml中添加 profile节点信息如下: <profiles> <profile> <!-- 开发环境 --> <id& ...

  4. JSON的一些小结

    一.js中 1.json字符串转json对象 var json = $.parseJSON(" {'1':'hello'},{'2':'word'} "); for(var i i ...

  5. 【SpringBoot】搜索框架ElasticSearch介绍和整合SpringBoot

    ========================12章 搜索框架ElasticSearch介绍和整合SpringBoot ============================= 加入小D课堂技术交 ...

  6. range的新发现

    正向打印的时候 for i in range(2): print(i) 打印的结果 0 1 反向的时候 for i in range(2,-1,-1): print(i) 2 1 0 for i in ...

  7. C++ Primer Plus (Stephen Prata 著)

    第1章 预备知识 (已看) 第2章 开始学习C++ (已看) 第3章 处理数据 (已看) 第4章 复合类型 (已看) 第5章 循环和关系表达式 (已看) 第6章 分支语句和逻辑运算符 (已看) 第7章 ...

  8. MyTomcat(手写服务器)

    Tomcat 是非常流行的 Web Server,它还是一个满足 Servlet 规范的容器.那么想一想,Tomcat 和我们的 Web 应用是什么关系? 从感性上来说,我们一般需要把 Web 应用打 ...

  9. centos7 下yum源安装nginx

    简单粗暴: .rpm -ivh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noar ...

  10. python学习路程1

    常用的转义字符还有: \n 表示换行 \t 表示一个制表符 \\ 表示 \ 字符本身 任务 请将下面两行内容用Python的字符串表示并打印出来: Python was started in 1989 ...