博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java反射机制详解上篇
阅读量:6328 次
发布时间:2019-06-22

本文共 5150 字,大约阅读时间需要 17 分钟。

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博客---

近期读书计划:

《思考,快与慢》

《浪潮之巅》

本文转自 维度2018 51CTO博客,原文链接:http://blog.51cto.com/xinsz08/1946912,如需转载请自行联系原作者

你可能感兴趣的文章
mysqldump导出--数据+结构+(函数+存储过程)
查看>>
浏览器的渲染原理简介
查看>>
获取系统当前时间参数date
查看>>
MySQL性能优化的最佳20+条经验
查看>>
exchange server 相关
查看>>
centos7系列安装vnc服务并授权用户访问
查看>>
CentOS mailx client
查看>>
字符串格式化
查看>>
Why Should You Choose Linux?
查看>>
NetScaler 12.1 发布
查看>>
checkpoint system management
查看>>
CentOS 6.5安全加固及性能优化_操作系统
查看>>
每天laravel-20160709|CallEvent
查看>>
我的友情链接
查看>>
【三石jQuery视频教程】02.创建 FontAwesome 复选框和单选框
查看>>
Cisco 配置DHCP中继 代理工程 实例
查看>>
Centos7.3部署KVM虚拟化环境
查看>>
configure: error: Cannot find ldap.h
查看>>
Linux启动分析(2)— bootsect.S、setup.S、head.S分析
查看>>
自学java时的笔记(一)
查看>>