java.lang.reflect
最近也是面试的时候问道一个问题,如何将一个java对象转换为json字符串,一听到的时候没有任何思路,之前也有接触过fastjson,知道就是用这个jar包来处理的,但是具体如何运行原理并不了解,导致面试说不出来,面试官提到fastjson其实就是利用反射来获取这个对象对应的信息,然后转化,于是对反射机制做一个详细的掌握。
一、什么是反射机制?
java的反射机制就是在java程序运行中,可以获得任何一个类的所有属性和方法。对于任意一个对象,可以调用其成员和方法。这种动态获取类信息和调用属性和方法的机制叫做java的反射机制。
要剖析一个类,最关键的是获取其字节码文件(.class)也就是Class对象信息。
下面我们通过java的类加载机制来分析一下Class对象怎么来的:
反射就是把java类中的各种成分映射成一个个的Java对象
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
如图是类的正常加载过程:反射的原理在与class对象。
熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。
整个过程其实就是类加载的加载过程,通过类的全限定名将磁盘中的class二进制文件加载到内存中,然后将其静态存储结构转化为方法区可以运行的数据结构,最后生成一个Class对象作为访问的入口。(这里就生成了Class对象就是包含了类的信息)
二、查看Class类在java中的api详解(1.7的API)
如何阅读java中的api详见java基础之——String字符串处理
Class
类的实例表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型)
Class
没有公共构造方法。Class
对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass
方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。
三、反射的使用
反射的使用依赖的关键就是Class对象,不管需要什么样的操作,都需要先拿到对应类或者对象的Class对象才能起作用。
1.获取Class对象的三种方法
- 通过静态.class方法获取,任何数据类型(包括基本数据类型)都有静态class() 方法直接获取Class对象,但是没有这个类就无法调用
- 通过getClass()方法,该对象的getClass()方法可以获取该类的Class文件,需要导入对象
- 通过class.forname(String name) 通过一个类的全限定名,拿到其Class对象,不需要任何支持
反射的常用类和函数:Java反射机制的实现要借助于4个类:Class,Constructor,field,Method;其中class代 表的是类对象,constructor-类的构造器对象,Field-类的属性对象,Method-类的方法对象,通过这四个对象我们可以粗略的看到一个类的各个组成部分。其中最核心的就是Class类,它是实现反射的基础
2.通过反射获取构造函数
测试类:
public class BeanDemo {
private int age;
private String name;
public String pub;
String tall;
char sex;
public String getTall() {
return tall;
}
public void setTall(String tall) {
this.tall = tall;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
/**唯一的方法
* */
public void speak(String name){
System.out.println("我的名字是"+name);
}
public BeanDemo() {
}
/**构造函数
* */
private BeanDemo(String name, int age) {
this.name = name;
this.age = age;
System.out.println("私有构造函数调用:"+name+age);
}
public BeanDemo(String name) {
this.name = name;
System.out.println("调用构造函数:"+name);
}
public BeanDemo(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
构造函数:
/**一:通过反射获取构造方法
* 1.通过getConstructors() 所有公有的构造方法
* */
Constructor[] constructors = bean.getConstructors();
System.out.println("====================================getConstructors()获取公有构造方法==========================");
for (Constructor c : constructors){
System.out.println("公有构造函数有:"+c);
}
System.out.println("====================================getDeclaredConstructors()获取私有,protected,默认,公有构造方法(也就是所有的构造方法)==========================");
/** 2.通过getDeclaredConstructors() 获取所有
* */
Constructor[] decCon = bean.getDeclaredConstructors();
for (Constructor c : decCon){
System.out.println("全部构造函数有:"+c);
}
System.out.println("====================================getConstructor(null) 无参构造函数 可以根据传入的参数类型获取对应的构造函数==========================");
Constructor nullCon = bean.getConstructor(null);
Constructor intCon = bean.getConstructor(String.class);
System.out.println(nullCon);
System.out.println(intCon);
System.out.println("====================================getConstructor(null) 调用构造函数==========================");
Object obj = intCon.newinstance("hhh");//调用构造函数,就是利用构造函数对象,创建实例
//若是私有构造函数,加上setaccessible(true) 要先用getDeclaredConstructor()
Constructor intStrCon = bean.getDeclaredConstructor(String.class,int.class);
intStrCon.setAccessible(true);//忽略访问限制
Object inStrObj = intStrCon.newInstance("String",100); //不加会抛出异常 java.lang.NoSuchMethodException: t201808.t20180831.BeanDemo.<init>(java.lang.String, int)
常用的获取构造函数方法:
- getConstructors()获取所有共有的构造方法
- getDeclaredConstructors()获取所有的构造方法,私有共有,protected 默认
- 调用构造函数就是利用获得的构造函数对象创建实例
3.通过反射获取成员变量
/**二:通过反射拿到成员变量
* 1.getDeclaredFields()可以拿到所有成员变量
* 2.getFields() 只能拿到公有的成员变量
* */
Field[] NoDecfields = bean.getFields(); //公有成员变量
Field [] fields = bean.getDeclaredFields();//公有私有都可以拿到
System.out.println("====================================getFields() 公有成员变量==========================");
for (Field f :NoDecfields){
System.out.println(f);
}
System.out.println("====================================getFields() 全部成员变量==========================");
for (Field f :fields){
System.out.println(f);
}
System.out.println("====================================getFields() 获取对应公有字段并调用==========================");
Field pubField = bean.getField("pub");
//首先要获取一个pub成员 有值的对象
Object object = bean.getConstructor(String.class,String.class).newInstance("name","这是我设置的PUB");
//获取到该字段,注意get()方法获取返回值为对象,还有getInt()等方法可调用
System.out.println(pubField.get(object));
//设置该成员变量
pubField.set(object,"新设置的PUB值");
System.out.println(pubField.get(object));
//Field的其他方法 getName()可以直接拿到属性的名字
System.out.println(pubField.getName());
//同理私有变量访问需要setAccessible(true) 忽略权限
常用的获取属性方法:
- getFields()获取所有共有的构造方法
- getDeclaredFields() 获取所有的构造方法,私有共有,protected 默认
- 获取属性值的时候,需要2个对象,一个是需要获取值的对象obj,一个属性值对象field。
- 通过调用field.get(obj) 可以获取到对象的对于属性值。
- 同样,若是私有属性需要setAccessible()
一个fastjson的对象转字符串的例子:
BeanDemo beanObj = new BeanDemo("hhhhh",99);
Class clazz = beanObj.getClass();
Field[] fields = clazz.getDeclaredFields();
System.out.println("{");
for (Field f : fields){
Field ageField = clazz.getDeclaredField(f.getName());
ageField.setAccessible(true);
System.out.println("\""+f.getName()+"\""+":"+ageField.get(beanObj));
}
System.out.println("}");
4.通过反射获取方法以及调用方法
/**三、通过反射获取方法以及调用方法
* */
Method[] pubMethods = bean.getMethods();
System.out.println("====================================获取公有方法 包括继承的方法 实现的==========================");
for (Method m :pubMethods){
System.out.println(m);
}
System.out.println("==================================== 获取全部方法,公有私有保护默认,不包括继承的方法==========================");
Method[] allMethods = bean.getDeclaredMethods();
for (Method method : allMethods){
System.out.println(method);
}
System.out.println("====================================获取指定方法=========================");
Method method = bean.getMethod("speak1", String.class);//参数列表为 方法名和方法的参数列表类型
System.out.println(method);
//调用该方法 invoke()
//需要先创建一个对象
Object obj = bean.getConstructor().newInstance();
method.invoke(obj,"?????");//方法的调用采用方法对象 调用invoke()方法,传入对象,以及方法需要的参数
//调用有返回值的方法
method = bean.getDeclaredMethod("speak4", int.class);
method.setAccessible(true);
System.out.println(method);
Object res =method.invoke(obj,12);
System.out.println(res);
相关阅读
我现在只是一个快2年经验的平凡的菜鸡boy 第一面架构师面试 1、简单介绍一下你经常使用的集合 2、什么是线程安全 3、arraylist和
MD5加密是一种常见的加密方式,我们经常用在保存用户密码和关键信息上。那么它到底有什么,又什么好处呢,会被这么广泛的运用在应用开
javascript通过navigator.userAgent识别各种浏览器
识别各种浏览器的实现原理是根据navigator.userAgent返回值识别: unction validBrowser(){ var u_agent = navigator.userAgent
了解和记录一下math类的各种方法,以便以后查找math.sin:求出sin值math.cos:求出cos值math.tan:求出tan值sinθ=cosθ*tanθmath.as
Queue: 基本上,一个队列就是一个先入先出(FIFO)的数据结构Queue接口与List、Set同一级别,都是继承了Collection接口。LinkedList实现了