一:什么是反射。
反射的概念:主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。
java
中的反射:
是
Java
程序开发语言的特征之一,它允许运行中的
Java
程序对自身进行检查,或者说
“
自审
”
,并能直接操作程序的内部属性和方法
总结:
简而言之
Java
的反射能在程序运行的时候,获取正在运行类中的属性和方法。并且还能
访问、检测和修改他们的能力。
二:反射的基础
Class
类。
Java
程序中的各个
java
类属于同一类事物,描述这类事物的
java
类名就是
Class
.
Class
的对象呢?有三种方法可以的获取:
、
调用
Object
类的
getClass()
方法来得到
Class
对象,这也是最常见的产生
Class
对象的方法
。
例如:
2
、
使用
Class
类的中静态
forName()
方法获得与字符串对应的
Class
对象。
例如:
必须是接口或者类的名字。
3
、
获取
Class
类型对象的第三个方法非常简单。如果
T
是一个
Java
类型,那么
T.class
就
代表了匹配的类对象。
例如
注意:
Class
对象实际上描述的只是类型,而这类型未必是类或者接口。例如上面的
int.class
是一个
Class
类型的对象。由于历史原因,数组类型的
getName
方法会返回奇怪的名字。
类的常用方法
、
getName()
Class
对象描述了一个特定类的属性,
Class
类中最常用的方法
getName
以
String
的形式返回此
Class
对象所表示的实体(类、接口、数组类、基本类型或
void
)
名称。
、
newInstance()
还有一个有用的方法可以为类创建一个实例,这个方法叫做
newInstance()
。
例如:
,创建了一个同
x
一样类型的新实例。
newInstance()
方法调
用默认构造器(无参数构造器)初始化新建对象。
、
getClassLoader()
的一些使用技巧
、
forName
和
newInstance
结合起来使用,可以根据存储在字符串中的类名创建对象。例
如
、虚拟机为每种类型管理一个独一无二的
Class
对象。因此可以使用
==
操作符来比较类
对象。例如:
三:什么是字节码
字节码是一种中间码,它比机械码更抽象。它经常被看作是包含一个执行程序的
二进制文件
,更像一个对象模型。在java中,
字节码
是
CPU
构架(JVM)
的具有可移植性的机械语言。
四:写
java
反射类一般的两步骤:
第一步:
获取类的字节码;获取方法有四种:
第一种:运用
getClass()
注:每个
class
都有此函数
第二种:运用
Class.getSuperclass()
通过子类求出父类的字节码
第三种:运用
Class.forName()
(最常被使用)
。通过用
Class
类中的方法
第四种:运用
primitive wrapper classes
的
TYPE
语法
,这个有局限性只能用于 九个预定义的对象:八个基本数据对象
+ void
举例:
String str1 = “abc”;
第一种:
Class cls1 = str1.getClass();
或者
Class cls2 = String.class;
第二种:Class c2 = cls1.getSuperclass();
第三种:
Class cls3 = Class.forName(“java.lang.String”);
第四种:Class c1 = Boolean.TYPE;
第二步:
使用
java
的
api
,常用的操作类有。
Field
类:简单的理解可以把它看成一个封装反射类的属性的类。
//构造一个含构造方法的变量
-
public
class
ReflectPoint {
-
private
int
x;
-
public
int
y;
-
public
ReflectPoint(
int
x,
int
y) {
-
super
();
-
this
.x = x;
-
this
.y = y;
-
}
-
}
得到某个类中的某个
public
成员变量:
例:
Field filedY = pt1. getClass.getField(
“y”);
打印某个类中的某个
public
成员变量:
System.out.println(fieldY.get(pt1));
得到某个类中的某个
private
成员变量:
Field filedX = pt1. getClass.getDeclaredfield(“x”);
打印某个类中的某个
private
成员变量:
fieldX.setAccessible(true);
System.out.println(fieldx.get(pt1));
Constructor
类:
Constructor
类则封装了反射类的构造方法。
得到某个类所有的构造方法:
例:
Constructor[] constructor = Class.forName(
“java lang String”).getCoustructor();
得到某一个的构造方法:
例:
Constructor constructor =
Class.forName(“java lang String”).getCoustructor(StringBuffer.class);
创建实例对象。
通常方式:
String str = new String(new StringBuffer(“abc”));
反射方式:
String str = (String)constructor.newInstance(new StringBuffer(“abc”));
Method
类:它是用来封装反射类方法的一个类。
//方法与对象时没有关系的。参数(方法的名字,参数类型)
String Str1 = “abc”;
获取某个对象的方法。
Method methodCharAt = String.
class
.getMethod(
“charAt”
,
int
.
class
);
调用某个对象方法中的方法。
//invoke意思是调用,方法对象中的方法。(对象名,传入的参数)
System.out.println(methodCharAt.invoke(Str1, 1));
//如果invoke(null, 1)第一个参数为null,则这个方法是静态方法。
五:数组与
Object
之间注意事项。
1.数组是基础类型(
int,byte,
short,long, char, double,float ,boolean )
的数组就不能存入
Object
数组内;
例:
int[] a1 = new int[]{1,2,3};
Object[] obj = a1;//
是错误的。
原因:
Object[]
内部要装的是
Object
类型的元素,而
int[] a1
内装的是
int
类型的数组
(
基本类型
)
,所以错误。
2.数组不是基本类型时,数组存入
Object
数组时要拆包。
例:
int[] a1 = new int[]{1,2,3};
String[] a4 = new String[]{“a”,”b”,”c”};
sop(Arrays.asList(a1));//
打印的是
[[I@3d4b7453]
sop(Arrays.asList(a4));//
打印的是
[a, b, c]
原因:
jdk1.4
中的
asList
接收的是
Object
的数组,因为是
int
类型,所以和
Object[]
数组对不上, 所以跳到
jdk1.5
版本中
T… a
可变参数的
Object,
但这
Object
当做是一个参数
五:数组的反射
1.
创建数组打印的类,所以要获取字节码,数组长度。
-
public
static
void
printObject(Object obj){
-
Class<?
extends
Object> clazz = obj.getClass();
-
if
(clazz.isArray()){
//假如是数组
-
int
len =Array.getLength(obj);
-
for
(
int
i =
0
; i < len; i++){
-
System.out.println(Array.get(obj, i));
-
}
-
}
else
{
-
System.out.println(obj);
-
}
-
}
2.
就是对数组就会拆包,对不是数组就打印。
-
printObject(a4);
-
printObject(
“xcv”
);
-
printObject(a1);
打印结果:
a
b
c
xcv
1
2
3
六:反射实现框架功能:
框架(
Framework
)
:
是整个或部分系统的可重用设计,表现为一组抽象
构件
及构件实例间交互的方法
;
另一种定义认为,框架是可被应用开发者定制的应用骨架。前者是从应用
方面而后者是从目的方面给出的定义。
框架与工具的区别:
工具被用户的类调用,而框架则是调用用户提供的类。
框架解决的核心问题:
1.框架要顾及以后写的类。
2.框架不能
new
实例对象,而是要用反射类获取。
框架举例:
-
package
fanshe2;
-
//用反射做框架
-
import
java.io.FileInputStream;
-
import
java.io.InputStream;
-
import
java.util.Collection;
-
import
java.util.Properties;
-
public
class
ReflectTest3 {
-
public
static
void
main(String[] args)
throws
Exception{
-
// TODO Auto-generated method stub
-
InputStream ips = ReflectTest3.
class
.getClassLoader().getResourceAsStream(
“config2.properties”
);
-
Properties props =
new
Properties();
-
props.load(ips);
-
ips.close();
//要马上关闭资源,因为这样系统资源会泄露。
-
-
String className = props.getProperty(
“className”
);
-
Collection collections = (Collection)Class.forName(className).newInstance();
-
ReflectPoint pt1=
new
ReflectPoint(
3
,
3
);
-
ReflectPoint pt2=
new
ReflectPoint(
5
,
5
);
-
ReflectPoint pt3=
new
ReflectPoint(
3
,
3
);
-
collections.add(pt1);
-
collections.add(pt2);
-
collections.add(pt3);
-
collections.add(pt1);
-
System.out.println(collections.size());
-
}
-
}
在反射中配置文件的设置方法:
//
配置文件名叫
config.properties
//
配置文件方式一:
——————————————————————————————-
//config2.properties
为相对路径,相对于当前工作目录
//InputStream ips = new FileInputStream(“config2.properties”);
//
绝对路径例:
(“d:\\config2.properties”);
//
真正实际用途是配置出来的。就是用完整的路径,但完整的不是硬编码,而是运算出来的。
getReal.path();
//
配置文件方式二:用类加载器
—————————————————————————-
//InputStream ips = ReflectTest3.class.getClassLoader().getResourceAsStream(“config2.properties”);
//
如果
(“config2.properties”)
这样写,则去
classPath
的根目录下逐个找
“config2.properties”
该文件。
//
如果
(“fanshe2/config2.properties”)
//
类加载器方法的缺点只能读取文件,不能写文件。方式一还能写文件用
OutputStream.
//
配置文件方式三:用类方法:
——————————————————————————-
InputStream ips = ReflectTest3.class.getResourceAsStream(“config2.properties”);
//
如果配置文件不再自己的包内,则直接写成:
//InputStream ips = ReflectTest3.class.getResourceAsStream(“fanshe22/config2.properties”);
//
如果所在的包名加入了
/
,则要完整的包名因为他是绝对路径了。
//InputStream ips = ReflectTest3.class.getResourceAsStream(”
完整的包路径
/
所在的包名
/config2.properties”);