1反射机制是什么
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
在面向对象的世界里,万事万物皆对象.在java语言里,静态的成员,普通数据类型是不是对象呢?
类又是谁的对象呢?
首先类是对象,类是java.lang.Class类的实例对象.
新创建一个Foo类
Foo这个类也是一个实例对象,是Class类的实例对象,这个对象在官网被称为(class type).
反射是java程序开发语言的特性之一,它允许运行中的java程序获取自身的信息,并且可以操作类或者对象内部的属性.
反射的核心是JVM在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。
了解反射其实需要了解JVM,不过一般的资料不会在这一部分讲到JVM,毕竟学习也是要从浅入深的.
2反射机制能做什么
反射机制主要提供了以下功能:
-
在运行时判断任意一个对象所属的类;
-
在运行时构造任意一个类的对象;
-
在运行时判断任意一个类所具有的成员变量和方法;
-
在运行时调用任意一个对象的方法;
-
生成动态代理。
注意;是运行时获取而不是编译时获取.其实很多时候我们直接用eclipse写代码忽略了编译的过程
在Eclipse,当我们输入一个点的时候(比如 a.) 编译器就会自动列出它的属性和方法,这里就会用到反射
3反射机制的相关API (在这里只说反射机制五种功能的前两种)
java的反射机制的实现要借助于4个类: class,Constructor,Field,Method;
通过一个对象获得完整的包名和类名
1
2
3
4
5
6
7
8
9
10
11
12
|
package
cn.xins08.boke;
public
class
TestReflect {
public
static
void
main(String[] args) {
TestReflect testReflect =
new
TestReflect();
System.out.println(testReflect.getClass().getName());
// 结果:cn.xins08.boke.TestReflect
}
}
|
实例化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
|
package
cn.xins08.boke;
public
class
TestReflect {
public
static
void
main(String[] args)
throws
Exception {
Class<?> class1 =
null
;
Class<?> class2 =
null
;
Class<?> class3 =
null
;
//第一种方式(在JDBC开发中常用此方法加载数据驱动)
class1 = Class.forName(
"cn.xins08.boke.TestReflect"
);
//第三种方式:java中任何一个对象都有getClass方法
class2 =
new
TestReflect().getClass();
//第三种方式java中任何一个对象都有class属性
class3 = TestReflect.
class
;
System.out.println(
"类名称: "
+ class1.getName());
System.out.println(
"类名称: "
+ class2.getName());
System.out.println(
"类名称: "
+ class3.getName());
// 打印结果:
/*
* 类名称: cn.xins08.boke.TestReflect 类名称: cn.xins08.boke.TestReflect 类名称:
* cn.xins08.boke.TestReflect
*/
}
}
|
获取一个对象的父类与实现的接口:
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
|
package
cn.xins08.boke;
import
java.io.Serializable;
public
class
TestReflect
implements
Serializable {
private
static
final
long
serialVersionUID = -2862585049955236662L;
public
static
void
main(String[] args)
throws
Exception {
Class<?> clazz = Class.forName(
"cn.xins08.boke.TestReflect"
);
// 取得父类
Class<?> parentClass = clazz.getSuperclass();
System.out.println(
"clazz的父类为:"
+ parentClass.getName());
// clazz的父类为: java.lang.Object
// 获取所有的接口
Class<?> intes[] = clazz.getInterfaces();
System.out.println(
"clazz实现的接口有:"
);
for
(
int
i =
0
; i < intes.length; i++) {
System.out.println((i +
1
) +
":"
+ intes[i].getName());
}
// clazz实现的接口有:
// 1:java.io.Serializable
}
}
|
创建实例:
通过反射来生成对象主要有两种方式:
(1)使用Class对象的newInstance()方法来创建Class对象对应类的实例。
1
2
|
Class<?> c = String.
class
;
Object str = c.newInstance();
|
初始化一个类,生成一个实例的时候,new与newInstance() 有什么区别?
区别在于创建对象的方式不一样,前者是使用类加载机制,那么为什么会有两种创建对象方式?这个就要从可伸缩、可扩展,可重用等软件思想上解释了。
newInstance: 弱类型。低效率。只能调用无参构造。
new: 强类型。相对高效。能调用任何public构造。
newInstance()是实现IOC、反射、面对接口编程 和 依赖倒置 等技术方法的必然选择,new 只能实现具体类的实例化,不适合于接口编程。
(2)先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这种方法可以用指定的构造器构造类的实例。
1
2
3
4
5
6
7
|
// 获取String所对应的Class对象
Class<?> c = String.
class
;
// 获取String类带一个String参数的构造器
Constructor constructor = c.getConstructor(String.
class
);
// 根据构造器创建实例
Object obj = constructor.newInstance(
"23333"
);
System.out.println(obj);
|
注意事项:反射会额外的消耗一定的系统资源,如果不需要动态的创建一个对象,那么就不需要用反射
在文章的最后我们讨论下工厂模式:
1
2
3
4
5
6
|
package
cn.xins08.boke;
public
interface
Fruit {
public
void
eat();
}
|
1
2
3
4
5
6
7
8
9
10
|
package
cn.xins08.boke;
public
class
Apple
implements
Fruit{
public
void
eat(){
System.out.println(
"吃苹果"
);
}
}
|
1
2
3
4
5
6
7
8
9
10
|
package
cn.xins08.boke;
public
class
Factory {
public
static
Fruit getInstance(String className){
if
(
"apple"
.equals(className)){
return
new
Apple();
}
return
null
;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
|
package
cn.xins08.boke;
public
class
FactoryDemo {
public
static
void
main(String[] args) {
Fruit f = Factory.getInstance(
"apple"
);
f.eat();
}
}
//返回的结果是"吃苹果"
|
这种是一个最简单的工厂设计模式,但是有一个很大的问题是,如果现在接口的子类增加了,那么工厂类肯定需要改.而造成这个问题的病因就是new,如果变成反射机制就不一样了.反射的实例化对象只需要包.类就可以了.
1
2
3
4
5
6
|
package
cn.xins08.boke;
public
interface
Fruit {
public
void
eat();
}
|
1
2
3
4
5
6
7
8
9
10
|
package
cn.xins08.boke;
public
class
Orange
implements
Fruit{
public
void
eat(){
System.out.println(
"吃橘子"
);
}
}
|
1
2
3
4
5
6
7
8
|
package
cn.xins08.boke;
public
class
Apple
implements
Fruit{
public
void
eat(){
System.out.println(
"吃苹果"
);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package
cn.xins08.boke;
public
class
Factory {
public
static
Fruit getInstance(String className){
Fruit fruit =
null
;
try
{
//这里是Fruit的类类型,做强制类型转换
fruit = (Fruit) Class.forName(className).newInstance() ;
}
catch
(Exception e) {
e.printStackTrace();
}
return
f ;
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
|
package
cn.xins08.boke;
public
class
FactoryDemo {
public
static
void
main(String[] args) {
Fruit f = Factory.getInstance(
"cn.xins08.boke.Apple"
);
f.eat();
}
}
|
这时候我们发现即使增加几个接口的子类,工厂类照样可以完成对象的实例化操作,可以对应所有的变化.
到目前为止你已经会了最基本的反射使用了,在学习Spring之前先讲这些,等学习了Spring的依赖注入,反转控制之后,相信会对反射有更好的理解.
关于反射以下功能,等写完后会在本文加链接
-
在运行时判断任意一个类所具有的成员变量和方法;
-
在运行时调用任意一个对象的方法;
-
生成动态代理。
--java一万小时成神之路--本文首发与51CTO博客---
近期读书计划:
《思考,快与慢》
《浪潮之巅》
java的反射机制一共分为上下两篇,反射机制下篇请参考:http://xinsz08.blog.51cto.com/10565212/1947327
本文转自xinsz08の平行时空博客51CTO博客,原文链接http://blog.51cto.com/xinsz08/1946912如需转载请自行联系原作者
维度2018