反射是Java中一个很是重要、很是强大的机制。曾看到一句话“反射是框架的灵魂”,初学时不懂,等到学完框架以后才慢慢理解其意。html
什么是反射?咱们先经过几个类和示例来初步体会一下反射。 java
1、ClassLoader类bootstrap
什么是类加载器?
数组
ClassLoader是一个抽象类,它的实例是类加载器。磁盘上存在的xxx.class文件须要被加载进JVM才能执行。类加载器则是负责加载.class文件的对象,而后在JVM中生成该类的Class对象。每个Class对象都关联着定义它的那个类加载器。数组的类加载器与其元素的加载器是同一个,若是元素类型是基本类型,则数组没有类加载器。框架
类加载器工做原理ide
类加载器都有一个与之关联的父加载器,当加载器须要加载一个文件时,它首先将该任务”委派”给父加载器,若是父加载器没法加载该文件,再本身进行加载。JVM的引导加载器(bootstrap class loader)没有父加载器,但可做为父加载器。关于类加载器更详细的分析 点击这里post
2、Class类测试
什么是Class<T>类?this
Class不是咱们日常声明类时所用的关键字class,它是一个类,它的对象用来描述一个运行状态的类或接口。一个xxx.java文件编译后生成一个xxx.class文件,一个xxx.class文件被JVM加载后生成该类对应的Class对象,该对象包含了该类的全部信息,好比,类中有字段、构造器、方法等信息。一个类有一个对应的Class对象,元素类型相同且长度相同的数组共享一个Class对象,java基本类型包括void也都有各自的Class对象。Class是一个泛型类,若是不加泛型,须要强转。spa
如何获取一个类的Class对象?
Class没有public构造器,当类被加载时,JVM会经过调用ClassLoader的defineClass方法来自动建立该类的Class对象。
获取一个类的Class对象有三种方式:
1)类名.class
2) 该类的对象.getClass()
3) Class.forName(String 类名) (包名加类名)
Class对象有何做用?
下面列出几个Class类的方法:
1) 获取类加载器:
getClassLoader()
2) 获取Constructor构造器对象:
getConstructor(Class... parameterType) 获取具备指定参数的公共构造器对象
getConstructors() 获取全部公共构造器对象
getDeclaredConstructor(Class... parameterType) 获取具备指定参数的构造器对象
getDdclaredConstructors() 获取全部构造器对象
3) 获取Method对象:
getMethod(String name,Class...parameterType) 获取具备指定名称和参数的公共方法对象
getMethods() 获取全部公共方法对象
getDeclaredMethod(String name,Class...parameterType) 获取具备指定名称和参数的方法对象
getDeclaredMethods() 获取全部的方法对象
4)获取Field字段对象:
getField(String name) 获取具备指定名称的公共字段对象
getFields() 获取具备全部公共字段对象
getDeclaredField(String name) 获取具备指定名称的字段对象
getDeclaredFields() 获取全部字段对象
5)获取Class对象所表明的类的一个对象(很是重要的一个方法)
Object newInstance() 用默认的无参数构造器建立一个对象
(带Declared的get方法能够获取任意访问权限的成员,不带Declared的只能获取public成员)
测试Class,更多的测试在其余类的测试中体现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
package
cn.edu;
public
class
User {
private
String username;
private
int
age;
public
User() {
}
public
User(String username ,
int
age) {
this
.username = username;
this
.age = age;
}
private
User(String username) {
this
.username = username;
}<br>
public
void
say(String str) {<br> System.out.println(str);<br> }<br><br>
@Override
public
String toString() {
return
"User [username="
+ username +
", age="
+ age +
"]"
;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
|
@Test
public
void
fun2()
throws
Exception {
Class userClass = User.
class
;
System.out.println(userClass.getName());
//获取该class对象的类的类名
System.out.println(userClass.getClassLoader().toString());
//获取类加载器
User user = (User)userClass.newInstance();
//调用默认的无参数构造器建立对象
System.out.println(user.toString());
}
|
运行结果:
3、Constructor类
构造器类,封装构造器的有关信息。
主要方法 Object newInstance(Object...arg) 用指定参数建立对象
测试Constructor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@Test
public
void
fun2()
throws
Exception {
Class userClass = User.
class
;
Constructor userConstructor1 = userClass.getConstructor(String.
class
,
int
.
class
);
//有参构造器
User user1 = (User)userConstructor1.newInstance(
"小红"
,
18
);
System.out.println(user1.toString());
Constructor userConstructor2 = userClass.getConstructor();
//无参构造器
User user2 = (User)userConstructor2.newInstance();
System.out.println(user2);
Constructor userConstructor3 = userClass.getDeclaredConstructor(String.
class
);
//私有构造器
userConstructor3.setAccessible(
true
);
//开启访问权限
User user3 = (User)userConstructor3.newInstance(
"小明"
);
System.out.println(user3);
}
|
运行结果:
默认是不能够访问private成员的,userConstructor3.setAccessible(true)是用于开启访问权限的,这样就能够访问了,感受是开挂同样!
4、Method类
方法类,封装方法的有关信息
主要方法
Object invoke(Object obj , Object... args) 调用obj对象的Method对象表明的方法,args为参数
测试Method:
1
2
3
4
5
6
7
8
|
@Test
public
void
fun2()
throws
Exception {
Class userClass = User.
class
;
User user = (User)userClass.newInstance();
//用默认无参数构造方法建立对象
Method method = userClass.getMethod(
"say"
,String.
class
);
//获取名为"say",参数为string的method对象
method.invoke(user,
"hello"
);
//调用user的say方法
}
|
运行结果:
5、Field类
字段类,封装字段的有关信息
主要方法
Object get(Object obj) 获取obj对象的此Field对象表明的字段的值
void set(Object obj , Object value) 设置obj对象的此Field对象表明的字段的值
测试Fidle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@Test
public
void
fun2()
throws
Exception {
Class userClass = User.
class
;
Constructor userConstructor = userClass.getConstructor(String.
class
,
int
.
class
);
User user = (User)userConstructor.newInstance(
"小明"
,
18
);
System.out.println(user.toString());
Field userField = userClass.getDeclaredField(
"username"
);
//获取username字段
userField.setAccessible(
true
);
//开启访问权限
userField.set(user,
"小红"
);
//给user对象的该字段设置值
System.out.println(user.toString());
}
|
运行结果:
到此关于反射的几个类就简单的认识了一下,咱们能够不用new关键字来建立对象,调用对象的方法也与传统的调用方式有很大区别,咱们甚至能够操做private成员(虽然这样作破坏了封装性),相对于传统的操做方式,反射更像是一种逆向思惟,之前操做成员,主体在于对象,而反射的主体在于Class和成员自己。到此咱们对反射有了初步的认识。接下在叙述一个重要的概念,有助于咱们更好的理解反射。
6、动态加载与静态加载
注意此处所说的加载是针对编译的,将xxx.java转化成xxx.class,而不是运行的加载字节码。Java建立对象的经常使用方式是使用new 关键字,如 User user = new User(); 这种是静态加载,即在编译期就已经获取User类的有关信息,若是User类不存在或有错误,编译会报错。动态加载就是用上面的Class.forName("包名.User");来加载User类,若是User类不存在或是有错误,在编译时是不会报错的,由于根本没去加载User类。只有当程序运行到该处,JVM才会去加载User,而动态加载则是依赖反射实现的。
好了,能够给出最终概念了,经过上面的重重示例与解析,应该不难理解反射了。
7、反射
Java反射机制是指java程序在运行过程当中,能够获取任意一个类的相关信息,而且可以调用其方法和属性,这种动态获取信息和动态调用方法的功能叫作Java的反射机制。
上面写了这么多,总结起来也就这一句话。
本文我的编写,水平有限,若有错误,恳请指出,欢迎讨论分享。