java反射机制历史_java反射原来是这么玩的(反射一开,谁都不爱)

反射的发展历史

1996年01月23日,jdk 1.0版本发布,代号为Oak(橡树)。

这个代号为Oak(橡树)的版本,在发布后的第二年,1997年02月19日,发布jdk 1.1版本,这次版本发布中引入了反射机制。

关于反射机制,由于年代久远,能搜索到对于反射机制的记载少之又少,能找到最为久远的是一篇题为《Using Java Reflection》的文章,发表于 1998年1月,文中提到:反射是一个可以获取java类、属性的一个工具,因为它是动态加载的。

而在另外一篇文章《A Button is a Bean》里解释道,反射是为了能把一个类的属性可视化的展示给用户,如下图所示:

1792fd180066e27ae9372d35f907b367.png

通俗的解释就是:无论是公有还是私有的方法、属性、构造方法,全都可以用反射进行获取、进行赋值、调用。听到这个解释,是不是感觉反射很强。

正因为反射的强大,在java世界里运用的地方有很多,比如:Java类加载和初始化、Java中RTTI、Spring的IOC,。

如此广泛的运用,只能说反射除了强,用起来肯定很爽。我想起我的同事,IT界的刁民,总是热衷于反射。

他在讲解他是如何运用反射时,嘴角总是压抑不住的微笑,这种迷恋反射的样子,像极了爱情。

正所谓:反射一开,谁都不爱。(傲娇)

下面就看看反射究竟是如何在程序中使用的。

反射的概述和使用

反射的概述

JAVA反射机制是在运行状态中,

对于任意一个类,都能够知道这个类的所有属性和方法;

对于任意一个对象,都能够调用它的任意一个方法和属性;

这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

我们知道class文件是在编译的时候生成的,Class对象是将class文件读入内存,并为之创建一个Class对象。

Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。

Class类里面,包含了一个类应有的所有描述,包括:

字段:Field.java

方法:Method.java

构造方法:Constructor.java

等等...

知道了Class类里面包含了哪些内容之后,再看一下new一个对象的究竟会发生那些过程:

05904935c56469f847832f8dc848b33f.png

反射的使用

这里使用一个Animal类来作为示范,可以看到这个类里的成员变量、方法、构造方法的访问修饰符既有public、也有private的。下面就将使用反射获取不同修饰符修饰的成员变量、方法、构造方法。

package com.shuai.ioc.ref;

public class Animal {

/**

* 动物名字

*/

public String name;

/**

* 动物年龄

*/

protected int age;

@Override

public String toString() {

return "Animal{" +

"name='" + name + '\'' +

", age=" + age +

'}';

}

/**

* 默认的构造方法

*

* @param name

*/

Animal(String name) {

System.out.println("执行了" + "默认的构造方法 " + name);

}

/**

* 无参构造方法

*/

public Animal() {

System.out.println("执行了" + "无参构造方法 ");

}

/**

* 有一个参数的构造方法

*

* @param name

*/

public Animal(char name) {

System.out.println("执行了" + "有一个参数的构造方法 name:" + name);

}

/**

* 有多个参数的构造方法

*

* @param name

* @param age

*/

public Animal(String name, int age) {

System.out.println("执行了" + "有多个参数的构造方法 name:" + name + "age:" + age);

}

/**

* protected的构造方法

*

* @param n

*/

protected Animal(boolean n) {

System.out.println("执行了" + "受保护的构造方法 n:" + n);

}

/**

* 私有构造方法

*

* @param age

*/

private Animal(int age) {

System.out.println("执行了" + "私有构造方法 age:" + age);

this.name = "私有构造方法调用成功";

this.age = age;

}

/**

* 公有方法

*

* @param s

*/

public void public1(String s) {

System.out.println("调用了" + "公有的方法" + ": public1 , s:" + s);

}

/**

* protected的方法

*/

protected void protected2() {

System.out.println("调用了" + "protected的方法" + ": protected2 ");

}

/**

* 友好的方法

*/

void friendly1() {

System.out.println("调用了" + "友好的方法" + ": friendly1 ");

}

/**

* 私有方法

*

* @param age

* @return

*/

private String private1(int age) {

System.out.println("调用了" + "私有方法" + ": private1 ,age:" + age);

return age + "";

}

}

用反射获取类的构造方法

在Class类中,提供一系列获取被反射类构造方法的方法。

批量获取构造方法的方法

public Constructor[] getConstructors():所有"公有的"构造方法

public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)

获取单个的方法,并调用

public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法

public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;

调用构造方法

newInstance(Object... initargs)

package com.shuai.ioc.ref;

import com.shuai.ioc.Book;

import java.lang.reflect.Constructor;

public class ConstructorsTest {

public static void main(String[] args) throws Exception {

//1.加载Class对象

Class clazz = Class.forName("com.shuai.ioc.ref.Animal");

//2.获取所有公有构造方法

System.out.println("所有公有构造方法");

Constructor[] conArray = clazz.getConstructors();

for (Constructor c : conArray) {

System.out.println(c);

}

// 所有的构造方法,公有、私有都行

System.out.println("");

System.out.println("所有的构造方法,包括:私有、受保护、默认、公有");

conArray = clazz.getDeclaredConstructors();

for (Constructor c : conArray) {

System.out.println(c);

}

// 获取公有、无参的构造方法

System.out.println("");

System.out.println("获取公有、无参的构造方法");

Constructor con = clazz.getConstructor(null);

System.out.println("con = " + con);

//调用构造方法

Object obj = con.newInstance();

// 获取私有构造方法

System.out.println("");

System.out.println("获取私有构造方法,并调用");

con = clazz.getDeclaredConstructor(int.class);

System.out.println(con);

//暴力访问,忽略掉访问修饰符

con.setAccessible(true);

//调用构造方法

Animal animal = (Animal) con.newInstance(1);

System.out.println(animal.toString());

}

}

用反射获取类的方法

在Class类中,提供一系列获取被反射类构造方法的方法。

批量的

public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)

public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)

获取单个的

public Method getMethod(String name,Class>... parameterTypes),name: 方法名;Class ...:形参的Class类型对象

public Method getDeclaredMethod(String name,Class>... parameterTypes),obj:要调用方法的对象;args:调用方式时所传递的实参;

调用方法

public Object invoke(Object obj,Object... args),obj:要调用方法的对象;args:调用方式时所传递的实参;

package com.shuai.ioc.ref;

import java.lang.reflect.Method;

public class MethodClassTest {

public static void main(String[] args) throws Exception {

//1.获取Class对象

Class stuClass = Class.forName("com.shuai.ioc.ref.Animal");

//2.获取所有公有方法

System.out.println("获取所有 公有 方法");

stuClass.getMethods();

Method[] methodArray = stuClass.getMethods();

for (Method m : methodArray) {

System.out.println(m);

}

System.out.println();

System.out.println("获取所有的方法,包括私有的");

methodArray = stuClass.getDeclaredMethods();

for (Method m : methodArray) {

System.out.println(m);

}

System.out.println();

System.out.println("获取公有的public1()方法");

Method m = stuClass.getMethod("public1", String.class);

System.out.println(m);

//实例化一个Student对象

Object obj = stuClass.getConstructor().newInstance();

m.invoke(obj, "this is name value");

System.out.println();

System.out.println("获取私有的private1()方法");

m = stuClass.getDeclaredMethod("private1", int.class);

System.out.println(m);

m.setAccessible(true);//解除私有限定

Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参

System.out.println("返回值:" + result);

}

}

用反射获取类的字段

在Class类中,提供一系列获取被反射类构造方法的方法。

批量的

Field[] getFields():获取所有的"公有字段"

Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;

获取单个的

public Field getField(String fieldName):获取某个"公有的"字段;

public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)

设置字段的值

public void set(Object obj,Object value):obj:要设置的字段所在的对象;value:要为字段设置的值;

package com.shuai.ioc.ref;

import java.lang.reflect.Field;

public class FieldsTest {

public static void main(String[] args) throws Exception {

//1.获取Class对象

Class animalClass = Class.forName("com.shuai.ioc.ref.Animal");

//2.获取字段

System.out.println("获取所有公有的字段");

Field[] fieldArray = animalClass.getFields();

for (Field f : fieldArray) {

System.out.println(f);

}

System.out.println();

System.out.println("获取所有的字段(包括私有、受保护、默认的)");

fieldArray = animalClass.getDeclaredFields();

for (Field f : fieldArray) {

System.out.println(f);

}

System.out.println();

System.out.println("获取公有字段并调用");

Field f = animalClass.getField("name");

System.out.println(f);

//获取一个对象

Object obj = animalClass.getConstructor().newInstance();//产生Student对象--》Student stu = new Student();

//为字段设置值

f.set(obj, "dog");//为Student对象中的name属性赋值--》stu.name = "刘德华"

//验证

Animal stu = (Animal) obj;

System.out.println("验证name:" + stu.name);

System.out.println();

System.out.println("获取私有字段并调用");

f = animalClass.getDeclaredField("name");

System.out.println(f);

f.setAccessible(true);//暴力反射,解除私有限定

f.set(obj, "this is name value");

System.out.println("验证name:" + stu);

}

}

反射越过泛型检查

编写代码时,如果我们设置容器list为String类型,在调用add方法插入数据时入参传了其他类型,编译时会无法成功,但是通过反射却可以执行,实例代码:

package com.shuai.ioc.ref;

import java.lang.reflect.Method;

import java.util.ArrayList;

import java.util.List;

/*

* 通过反射越过泛型检查

*

*/

public class IgnoreType {

public static void main(String[] args) throws Exception {

List list = new ArrayList<>();

list.add("one");

//反射获取list对象

Class listClass = list.getClass();

// 调用list对象的add方法

Method m = listClass.getMethod("add", Object.class);

m.invoke(list, 100);

//输出验证

for (Object obj : list) {

System.out.println(obj);

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值