1,反射:反射的基石---Class类
-------------java1.2就有的特性
1,反射的基石---Class类,代表一类什么样的事物?2,Class类的实例对象是什么?描述java程序中的各个java类Class clx = 字节码;Class的实例对象就是某个类的字节码.Class类中没有构造函数当内存用到Person.java时,首先要先编译时生成的字节码Person.class加载到内存中,然后再用类的字节码Person.class复制出多个对象,而内存中的每一份字节码就是Class类的一个实例对象
3,如何得到各个字节码对应的实例对象?
Class cls = ?//xxx.classs
a: 类名.classb: 对象.getClass( ),例如:new Person().getClass( );4,Class.forName( )的作用?c: 【反射时用到】Class.forName("类名");例如:Class.forName("java.util.Date");
Class.forName(String className);
调用 forName("X") 将导致命名为 X 的类被初始化static{ }静态代码块会被执行
Class.forName( )静态方法的目的是为了动态加载类Class.forName("");的作用是要求JVM查找并加载指定的类,如果在类中有静态初始化器的话,JVM必然会执行该类的静态代码段在开发数据库开发时会用到Class.forName(Sring className);//返回是字符串className代表的类的字节码str.getClass( );//找到对象str对应的类的字节码
123String sx = args[
0
];
//com.itcast.day01.TestArguments
System.out.println(Class.forName(sx));
//class com.itcast.day01.TestArguments
System.out.println(sx.getClass());
//class java.lang.String
5,
有九种预定义的 Class 对象,表示八个基本类型和 void
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
String str1 =
"abc"
;
Class cls1 = String.
class
;
Class cls2 = str1.getClass();
Class cls3 = Class.forName(
"java.lang.String"
);
System.out.println(cls1==cls2);
//true
System.out.println(cls1==cls3);
//true
System.out.println(cls1.isPrimitive());
//false
System.out.println(
int
.
class
.isPrimitive());
//true
System.out.println(
int
.
class
== Integer.
class
);
//fasle
System.out.println(
int
.
class
== Integer.TYPE);
//true
System.out.println(
int
[].
class
.isPrimitive());
//fasle
System.out.println(
int
[].
class
.isArray());
//true,判断是否是一个数组字节码
|
-----------------------------------------------------------------------------------------------------------总之,任何出现在源程序的类型,都有各自的Class实例对象,例如int[ ], void
2,反射-----就是把java类中的各种成分映射成相应的java类
---------------
1,反射的价值和作用?
2,构造方法的反射应用--Constructor类
1.得到某个类的所有的构造函数:Constructor[ ] cons = Class.forName("java.lang.String").getConstructors( );2.得到 某一个构造函数:3.创建实例对象
12Constructor con1 = Class.forName(
"java.lang.String"
).getConstructor(StringBuffer.
class
);
String str2 = (String)con1.
newInstance
(
new
StringBuffer(
"abc"
));
通常方式:
1String str =
new
String(
new
StringBuffer(
"abc"
));
反射方式:
12Constructor con1 = Class.forName(
"java.lang.String"
).getConstructor(StringBuffer.
class
);
String str2 = (String)con1.newInstance(
new
StringBuffer(
"abc"
));
4.Class.newInstance( )方法
反射会导致计算机性能下降
@1:Class---->Constructor----->new object@2:Class------------------------->new object例子:
1String obj = (String)Class.forName(
"java.lang.String"
).newInstance();
1...该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象.2...该方法内部的具体代码-----用到了缓存机制来保存默认构造方法的实例对象
3,成员变量的反射应用
publicprivate-----暴力反射
默认权限----暴力反射
123456789public
class
ReflectPoint {
private
int
x;
//注意修饰符
public
int
y;
//
public
ReflectPoint(
int
x,
int
y) {
super
();
this
.x = x;
this
.y = y;
}
}
12345678910ReflectPoint pt1 =
new
ReflectPoint(
3
,
4
);
Field fieldY = pt1.getClass().getField(
"y"
);
//fieldY的值是多少? 是5,错!
//fieldY不是对象身上的变量,而是类上,要用它去取某个对象上对用的值
System.out.println(fieldY.get(pt1));
//结果4
-----------------------------------------------
Field fieldX = pt1.getClass().getDeclaredField(
"x"
);
fieldX.setAccessible(
true
);
System.out.println(fieldX.get(pt1));
//包里反射
4,成员变量的反射的综合案例
字节码要用"=="不用equals!!!
案例需求:将任意一个对象中的所有String类型的成员变量所对用的字符串内容总的"b"改成"a"
5.成员方法的反射
123456789101112private
static
void
changeStringValue(Object obj)
throws
Exception{
Field[] fields = obj.getClass().getFields();
for
(Field field:fields){
//if(field.getType().equals(String.class)){
//为什么用注释?让面试HR看到你用"=="是经过考虑的
if
(field.getType() == String.
class
){
String oldValue = (String)field.get(obj);
String newValue = oldValue.replace(
'b'
,
'a'
);
field.set(obj,newValue);
}
}
}
123//str1.charAt(1),反射做法如下
Method methodCharAt = String.
class
.getMethod(
"charAt"
,
int
.
class
);
System.out.println(methodCharAt.invoke(str1,
1
));
注意:如果传递给Method对象的invoke( )方法的第一个参数为null,这有什么意义?说明该Method对象对应的是一个静态方法
jdk1.4和jdk1.5的invoke方法的区别:jdk1.5:public Object invoke(Object obj,Object... args)jdk1.4:public Object invoke(Object obj,Object[ ] args)
12345methodCharAt.invoke(str1,
new
Object[]{
1
});
methodCharAt.invoke(str1,
new
String[]{
"111"
,
"222"
,
"333"
});
//JVM会拆箱,认为你给他传递了3个String参数,而不是一个数组参数
methodCharAt.invoke(str1,
new
Object(
new
String[]{
"111"
,
"222"
,
"333"
}));
//再装箱
methodCharAt.invoke(str1,(Object)
new
String[]{
"111"
,
"222"
,
"333"
});
//或者这种方法
6,反射方式执行某个类中的main方法
需求:写一个程序,根据用户提供的类名,去执行该类中的main()方法解析:1,用普通方法可以,那为什么用反射方式?
类名.mian(new String[]{"111","222","333"});//普通方式实际情况是你传一个参数,根本不知道要执行哪一个类
1
2
3
4
|
//TestArguments.main(new String[]{"111","222","333"});
String startingClassName = args[
0
];
//主函数接收的参数,需要在运行时传进去
Method mainMethod = Class.forName(startingClassName).getMethod(
"main"
,String[].
class
);
mainMethod.invoke(
null
,(Object)
new
String[]{
"111"
,
"222"
,
"333"
});
|
7,数组的反射
1.相同的元素类型,相同的维度属于同一个类型,即具有相同的Class实例对象
1234567int
[] a1 =
new
int
[
3
];
int
[] a2 =
new
int
[
4
];
int
[][] a3 =
new
int
[
2
][
3
];
String[] a4 =
new
String[
3
];
System.out.println(a1.getClass() == a2.getClass());
//true
//System.out.println(a1.getClass() == a4.getClass());//类型不匹配
//System.out.println((Object)a1.getClass() == (Object)a4.getClass());//false,强制转换后
2,数组.getClass( ).getSuperclass( )
3,基本数据类型的一维数组可以被当作Object类型使用,不可当作Object[ ]类型使用
4,Arrays.asList( )方法处理int[ ]和String[ ]时的差异非基本数据类型既可以当作Object类型使用,又可以单做Object[ ]类型使用
12345Object obj1 = a1;
//Object[] obj2 = a1;
Object[] obj3 = a3;
Object obj4 = a4;
Object[] obj5 = a4;
5,Array工具类用于完成堆数组的反射操作
2345678910private
static
void
printTest(Object obj) {
if
(obj.getClass().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);
}
}
6,思考:怎么得到数组总的元素类型?
答案:无法得到,Object[ ] a = new Object[ ]{"abc", 123} ;//无法确定Obejct的类型;只能确定具体元素的类型
8,ArrayList_HashSet的比较及HashCode分析
12345678910111213141516171819202122ReflectPoint pt1 =
new
ReflectPoint(
3
,
3
);
ReflectPoint pt2 =
new
ReflectPoint(
5
,
5
);
ReflectPoint pt3 =
new
ReflectPoint(
3
,
3
);
//pt1和pt3的不同Collection con1 =
new
ArrayList();
con1.add(pt1);
con1.add(pt2);
con1.add(pt3);
con1.add(pt1);
System.out.println(con1.size());
//4
//复写hashCode方法,结果:4
Collection con2 =
new
HashSet();
con2.add(pt1);
con2.add(pt2);
con2.add(pt3);
con1.add(pt1);
System.out.println(con2.size());
//3,set不重复
//复写hashCode方法,结果:2,哈希值只对哈希算法集合有效
1.hachCode( )方法的作用?
(1),为了提高查找元素的效率(HashSet集合)假如在HashSet中有10000个元素,现在我们往里面插入一个元素,那么我们要遍历10000次逐一比较;为了提高效率,有人发明了哈希算法来提高从集合中查找元素的效率算法实现:
集合------->分成若干个存储区域;根据一个对象的哈希码就可以确定该对象应该存储在那个区域每个对象----->计算出一个哈希码;哈希码分组,-------->每组对应某个存储区域
(2).内存泄露问题-----内存泄露:该内存空间使用完毕之后未回收(没有释放已无法到达的内存)!
当一个对象被储存进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段否则对象修改后的哈希值与最初不同,导致去HashSet中检索对象,也将返回找不到对象的结果这将导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露
123456con2.remove(pt1);
System.out.println(con2.size());
//删除了,大小是1
pt1.y =
7
;
con2.remove(pt1);
//运行后,由于哈希值变化,未索引到,没有删除
System.out.println(con2.size());
//大小是2
2,hashCode( )方法存在有价值的前提?
存储的对象必须在哈希算法集合中,如果存储在Lis集合中无任何作用.也就是说,
3,java中有内存泄露吗?
1,答案见上面2,输入输出流不close,导致流调用的底层资源没有释放
9,反射的作用---主要用它来做框架
1,先分清楚框架和工具类先举个例子:我做房子卖给用户主,用户自己安装门和空调;我做的房子就是框架,,用户需要使用我的框架,把门窗插入我提供的框架中变成自己的房子框架和工具区别:Tools类被用户调用,框架则调用用户提供的类2,框架要解决的核心问题
因为写程序时无法知道被调用的类名,所以,在程序中无法直接new某个类的实例对象,此时用到反射方式来做
3,综合案例
123456789101112131415//Collection conllections = new ArrayList();//
//用反射的方式
InputStream ips =
new
FileInputStream(
"config.properties"
);
Properties props =
new
Properties();
props.load(ips);
ips.close();
String className = props.getProperty(
"className"
);
Collection conllections = (Collection)Class.forName(className).newInstance();
//用反射范式得到对象,可以操作数据
conllections.add(pt1);
conllections.add(pt2);
conllections.add(pt3);
conllections.add(pt1);
System.out.println(conllections.size());
4,Properties类------父类:java.util.HashTable;
--------Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载String getProperty(String key) ----用指定的键在此属性列表中搜索属性.void load(InputStream inStream) -------从输入流中读取属性列表(键和元素对)
5, 配置文件的问题
第一种:可以读写,而第二种只能读,不能写
new-------->file-------->config.propertiesInputStream ips =
new
FileInputStream(
"config.properties"
);
Properties props =
new
Properties();
实际开发中,要亚勇绝对路径.getRealPath( );//项目"金山词霸"路径-------->/项目内部
一定要记住用完整的路径,但完整的路径不是硬编码,而是运算出来的
第二种:类加载器的方式方式: ---------- 只能读不能写
类加载器可以加载.class文件,那么同时提供了方法加载其他文件
1
2
3
4
5
6
7
8
9
10
11
|
//用反射的方式
//InputStream ips = new FileInputStream("config.properties");
//用加载器的方式
InputStream ips =
ReflectTest2.
class
.getClassLoader().getResourceAsStream(
"com/itcast/day01/config.properties"
);
Properties props =
new
Properties();
props.load(ips);
ips.close();
String className = props.getProperty(
"className"
);
Collection conllections = (Collection)Class.forName(className).newInstance();
|
1
2
3
4
5
|
//用加载器的方式
InputStream ips =
ReflectTest2.
class
.getClassLoader().getResourceAsStream(
"com/itcast/day01/config.properties"
);
InputStream ips = ReflectTest2.
class
.getResourceAsStream(
"config.properties"
);
//用工程包里的
InputStream ips = ReflectTest2.
class
.getResourceAsStream(
"resource/config.properties"
);
//resource文件夹中的
|
10,内省(introspector)---主要对javaBean操作
javaBean--->特殊的java类
123456789class
Person{
private
int
x;
public
void
getAge(){
return
x;
}
public
int
setAge(
int
age){
this
.x = age;
}
}
我们队类中的成员变量是看不到的,从方法上我们知道是对age操作,不是对"x"
如果要在两个模块之间快递多个信息,可以将这些信息分装到一个javaBean中,这种javaBean的实例对象通常称之谓值对象(Value Object,简称OV)java EE开发中经常使用到javaBean
JDK中提供了对javaBean进行操作的一些API----这套API就称为内省
11,javaBean的简单内省操作
1,把一个类变成javaBean.---->私有属性添加get和set方法.2.读取javaBean对象的属性3,java.beans.PropertyDescriptor 类( 属性描述符),通过属性该属性的描述4,用属性描述符得到get方法5,得到的Method对象invoke
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public
class
IntroSpector {
public
static
void
main(String[] args)
throws
Exception{
ReflectPoint pt1 =
new
ReflectPoint(
3
,
4
);
//想获取对象中的某个属性
String propertyName =
"x"
;
//面试题
//普通做法:"x"--->"X"---"getX"-----"MethodGetX"
//现在用内省方法
PropertyDescriptor pd =
new
PropertyDescriptor(propertyName,pt1.getClass());
Method methodGetX = pd.getReadMethod();
Object retVal = methodGetX.invoke(pt1);
System.out.println(retVal);
Object value =
7
;
//装箱
Method methodSetX = pd.getWriteMethod();
methodSetX.invoke(pt1,value);
System.out.println(pt1.getX());
}
}
|
这段代码的技巧:
变量的取值:1,方法参数的起名字就叫那个值Class.forName(String className)2,调用方法返回值Object retVal--------return value之意3,eclispe可以吧上面代码抽取(Extract)成方法:还有一种用BeanInfo方法实现的----稍微复杂的
12345678private
static
Object getProperty(Object pt1, String propertyName)
throws
Exception {
PropertyDescriptor pd =
new
PropertyDescriptor(propertyName,pt1.getClass());
Method methodGetX = pd.getReadMethod();
Object retVal = methodGetX.invoke(pt1);
return
retVal;
}
123456789101112131415161718private
static
Object getProperty(Object pt1, String propertyName)
throws
Exception {
/*PropertyDescriptor pd =
new PropertyDescriptor(propertyName,pt1.getClass());
Method methodGetX = pd.getReadMethod();
Object retVal = methodGetX.invoke(pt1);*/
BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass());
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
Object retVal =
null
;
for
(PropertyDescriptor pd : pds){
if
(pd.getName().equals(propertyName)){
Method methodGetX = pd.getReadMethod();
retVal = methodGetX.invoke(pt1);
break
;
}
}
return
retVal;
}
12,BeanUtils工具包
BeanUtils工具操作javaBean.Apache开发的
在用到javaBean.jar包的时候要导入logjar包
1,不确定....................................?jdk新特自己测试
123//JDK新特性
Map map = {name:
"zxx"
,age:
12
};
BeanUtils.setProperty(map,
"name"
,
"luj"
);
2,BeanUtils和PropertyUtils
commons.beanutils.BeanUtilscommons.beanutils.PropertyUtils
3,BeanUtils中的方法
12345678BeanUtils.setProperty(pt1,
"x"
,
"9"
);
System.out.println(BeanUtils.getProperty(pt1,
"x"
).getClass());;
PropertyUtils.setProperty(pt1,
"x"
,
9
);
System.out.println(PropertyUtils.getProperty(pt1,
"x"
).getClass());;
//7
//9
//class java.lang.String
//class java.lang.Integer
void copyProperties(Object dest, Object orig)
Map<String,String> describe(Object bean);// beab--->mappopulate(Object bean, Map<String,? extends Object> properties)// Map-------->.bean
123//JDK新特性
Map map = {name:
"zxx"
,age:
12
};
BeanUtils.setProperty(map,
"name"
,
"luj"
);