(一)Java类
Java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则是由这个类的实例对象来确定的,不同的实例对象有不同的属性值。
Java程序中的各个Java类,它们是否属于同一类事物,是不是可以用一个类来描述这类事物呢?
这个类的名字就是Class,
类的名字,类的访问属性,类所属于的包名,字段名称的列表、方法名称的列表,等等。学习反射,首先就要明白Class这个类。
2、数组类型的Class实例对象:
Class.isArray()
String str1="abc";
Class cls1=str1.getClass();
Class cls2=String.class;
Class cls3=Class.forName("java.lang.String");
//这三个字节码在内存中对应的是同一个
System.out.println(cls1==cls2);
System.out.println(cls1==cls3);
System.out.println(cls1.isPrimitive());//isPrimitive是否是原始类型
System.out.println(cls1);
System.out.println(int.class.isPrimitive());
System.out.println(int.class==Integer.class);
System.out.println(int.class==Integer.TYPE);//TYPE代表包装类型包装的基本类型字节码
System.out.println(int[].class.isPrimitive());
System.out.println(int[].class.isArray());
表示java类的Class类提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,
这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等
(1)Constructor类
//获得方法时要用到类型
//调用获得的方法时要用到上面相同类型的实例对象
//new String(new StringBuffer("abc"));
Constructor constructor1=String.class.getConstructor(StringBuffer.class);
/*JDK1.5的一个新特性:getConstructor里面可以指定若干个class对象,
每一个class对象代表一个类型
StringBuffer表示选择哪个构造方法
String类的字节码
指定内容
编译,翻译成class
*/
String str2=(String)constructor1.newInstance(new StringBuffer("abc"));
//拿String类字节码构造的对象
//实例化
//StringBuffer表示用上边这个构造方法时还要传进一个StringBuffer对象进去
System.out.println(str2.charAt(2));
该方法内部用到了缓存机制来保存默认构造方法的实例对象。
路径:D:\Program Files\Java\jdk1.6.0_10下 src.zip文件 打开src.zip\java\lang Class.java 找到newInstance0
public class ReflectPoint {
private int x;
public int y;
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class ReflectTest {
/**
* @param args
* @throws ClassNotFoundException
* @throws NoSuchMethodException
* @throws SecurityException
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws InstantiationException
* @throws IllegalArgumentException
* @throws NoSuchFieldException
*/
public static void main(String[] args) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchFieldException {
// TODO Auto-generated method stub
ReflectPoint pt1=new ReflectPoint(3,5);
Field fieldY=pt1.getClass().getField("y");//字节码可以getField,getField只能得到可见的
//fildY的值是多少?fieldY不是对象身上的变量,而是类上,要用它去取某个对象上对应的值
System.out.println(fieldY.get(pt1));
Field fieldX=pt1.getClass().getDeclaredField("x");//x不可见所以要用getDeclaredField
fieldX.setAccessible(true);//暴力反射,设置成可以访问,没有这一步fieldX.get(pt1)通不过
System.out.println(fieldX.get(pt1));
}
}
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class ReflectTest {
/**
* @param args
* @throws ClassNotFoundException
* @throws NoSuchMethodException
* @throws SecurityException
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws InstantiationException
* @throws IllegalArgumentException
* @throws NoSuchFieldException
*/
public static void main(String[] args) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchFieldException {
// TODO Auto-generated method stub
String str1="abc";
Class cls1=str1.getClass();
Class cls2=String.class;
Class cls3=Class.forName("java.lang.String");
System.out.println(cls1==cls2);
System.out.println(cls1==cls3);
System.out.println(cls1.isPrimitive());
System.out.println(cls1);
System.out.println(int.class.isPrimitive());
System.out.println(int.class==Integer.class);
System.out.println(int.class==Integer.TYPE);
System.out.println(int[].class.isPrimitive());
System.out.println(int[].class.isArray());
//new String(new StringBuffer("abc"));
Constructor constructor1=String.class.getConstructor(StringBuffer.class);
String str2=(String)constructor1.newInstance(new StringBuffer("abc"));
System.out.println(str2.charAt(2));
ReflectPoint pt1=new ReflectPoint(3,5);
Field fieldY=pt1.getClass().getField("y");//getField只能得到可见的
//fildY的值是多少?fieldY不是对象身上的变量,要用它去取某个对象上对应的值
System.out.println(fieldY.get(pt1));
Field fieldX=pt1.getClass().getDeclaredField("x");
fieldX.setAccessible(true);//暴力反射,设置成可以访问
System.out.println(fieldX.get(pt1));
}
}
eg:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class ReflectTest {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
changeStringValue(pt1);
System.out.println(pt1);
}
private static void changeStringValue(Object obj) throws Exception{
// TODO Auto-generated method stub
Field[] fields=obj.getClass().getFields();
for(Field field: fields){
//if(field.getType().equals(String.class)) 因为是string类型,所以==比较好
if(field.getType()==String.class)
{
String oldValue=(String)field.get(obj);
String newValue=oldValue.replace('b', 'a');
field.set(obj, newValue);
}
}
}
}
public class ReflectPoint {
public String str1="ball";
public String str2="basketball";
public String str3="itcast";
@Override
public String toString()
{
return str1+":"+str2+":"+str3;
}
}
如果传递给Method对象的invoke()方法的第一个参数为null,说明该Method对象对应的是一个静态方法
String str1="abc";
Method methodCharAt=String.class.getMethod("charAt", int.class);
System.out.println(methodCharAt.invoke(str1, 1));
//jdk1.4
System.out.println(methodCharAt.invoke(str1,new Object[]{2}));
根据用户提供的类名,去执行该类中的main方法
1、用静态代码的方法调用main方法:
public class ReflectTest {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
TestArguments.main(new String[]{"111","222","333"});
}
class TestArguments{
public static void main(String[] args){
for(String arg : args){
System.out.println(arg);
}
}
}
2、用反射执行
按F2,会出现完整包名
右键->run as ->open configurations...(打开运行对话框)
main标签指运行的那个类,Arguments指要传递的参数,例子中要传的参数是:cn.itcast.day1.TestArguments
相当于在运行里执行:java ReflectTest cn.itcast.day1.TestArguments (注:把cn.itcast.day1.TestArguments这个字符串传给了类ReflectTest)
用反射执行的原因:
启动Java程序的main方法的参数是一个字符串数组,
即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?
按jdk1.5的语法,整个数组是一个参数,
而按jdk1.4的语法,数组中的每个元素对应一个参数,
当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?
jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。
所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题
eg:
String startingClassname=args[0];
Method mainMethod=Class.forName(startingClassname).getMethod("main",String[].class);
//mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}});//把整个数组装成一个包
mainMethod.invoke(null, (Object)new String[]{"111","222","333"});
(六)数组的反射
/* int[] a1=new int[3];
int[] a2=new int[4];
int[][] a3=new int[2][3];
String[] a4=new String[4]*/;
int[] a1=new int[]{1,2,3};
int[] a2=new int[4];
int[][] a3=new int[2][3];
String[] a4=new String[]{"a","b","c"};
System.out.println(a1.getClass() == a2.getClass());
//System.out.println(a1.getClass() == a4.getClass());
//System.out.println(a1.getClass() == a3.getClass());
System.out.println(a1.getClass().getName());//结果[I :[代表数组;I代表int类型
System.out.println(a1.getClass().getSuperclass().getName());//获取父类 结果: java.lang.Object
System.out.println(a4.getClass().getSuperclass().getName());
Object aObj1=a1;
Object aObj2=a4;
//Object[] aObj3=a1;//数组里装的是int类型的,不是Object类型的,a1是基本数据类型int的,不是Object
Object[] aObj4=a3;//Object数组里装的是一维数组
Object[] aObj5=a4;
System.out.println(a1);//[I@1f33675 int类型的[数组的哈希code值1f33675
System.out.println(a4);//[Ljava.lang.String;@7c6768
System.out.println(Arrays.asList(a1));//1.4 asList(Object[] a);1.5asList(T...a)当成一个object
System.out.println(Arrays.asList(a4));
public static void main(String[] args) throws Exception {
printObject(a4);
printObject("xyz");
}
private static void printObject(Object obj) {
// TODO Auto-generated method stub
Class 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);
}
}
(六)反射的作用(实现框架的功能)
使用类时:一种是你调用别人的类,一种是别人调用你的类,这两种类一个叫框架,一个叫工具
eg:我去买房子,用的是开发商提供的房子,做门的时候用别人做的锁,两个东西用法不一样,锁被门窗调用,房子就是框架,卖给谁都行,谁买了装上自己的窗户和门就成了自己的家。所以你用框架,框架在调用你的类。而锁是一个工具类,你在调用锁。
用框架效率高,框架是一个半成品
HashSet用反射做
Collection collections=new HashSet(); HashSet()这个位置不是类的名字,而是配置文件读取出来的
新建文件,config.properties
source内容是
className=java.util.ArrayList
ReflectTest2.java文件里加载properties文件用
InputStream ips=ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");
Properties属性文件在JAVA应用程序中是经常可以看得见的,也是特别重要的一类文件。它用来配置应用程序的一些信息,不过这些信息一般都是比较少的数据,没有必要使保存,而使用一般的文本文件来保存,如果是通过File直接保存的话,可能在存储和读取上都不是很方便,但如果保存为Properties文件就不一样了,属性文件都有键值对应的,在JAVA的包中,有提供专门的操作属性文件的类。这个类就是java.uitl.Properties类,由于Properties类是一个集合类,所以,Properties会将属性以集合的方式读写。
//读取属性文件流的方法
public void load(InputStream inStream) throws IOException {}
//写属性文件流的方法
public void store(OutputStream out, String comments) throws IOException {}
我们要做的第一步就是要将文件读取到Properties类对象中,由于load有一个参数是InputStream,所以我们可以用InputStream的子类FileInputStream将属性文件读取到Properties对象中,知道config.properties的路径, 我们就用FileInputStream(String name)构造函数:
InputStream ips=ReflectTest2.class.getResourceAsStream("/cn/itcast/day1/resource/config.properties");//从根开始的路径
/* Properties等效一个HashMap(key yalue),
* 但在HashMap上扩展了功能,可以把 自己内存中的键值对存到硬盘中
* 也可以在初始化的时候,从一个文件把自己的键值对加载进来,*/
Properties props=new Properties();
/* 建完Properties马上用load(读取属性文件流的方法)读取config.properties文件流*/
props.load(ips);
/*
* 马上关门,良好的习惯,如果没有关门:就会有一点小小的内存泄露,ips关联的系统资源没有被释放
* 这个内存泄露不是ips对象没有被释放,而是ips关联的系统资源没有被释放,
* 例:
* 本对象由java虚拟机作为垃圾进行回收
* 自己在被垃圾回收之前,先把自己关联的系统资源,物理资源干掉
* 如果不干掉,就是ips对象没了,但是以前只指向操作系统的资源还在
* ips由虚拟机管理的,有垃圾回收器来管理的,这样就把资源释放了
*/
ips.close();
String className=props.getProperty("className");//反射
Collection collections=(Collection)Class.forName(className).newInstance();//得到了类.调用不带参数的构造方法
/*
* 如果把ReflectPoint里的hashCode删掉,其他不变
* 可能会输出3
* 如果虽然两个对象pt1,pt2比较想等了,但是算的hashcode值,是按照你的内存算的,这
* 两个本来相同的值因为按照内存计算的
* 因为pt1与pt3取得区域可能不一样
* pt1会有内存值,pt3也会有内存值,这两个可能会相等
* 但只在一个区域里找*/
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);
/*
* collection里存完有两个值
* 移去一个应该还有一个,但打印出来是2
* 说明存完再改值后hashcode值也改了,不再一个区域内
* 这样就内存泄漏了
* 假如程序不断的增加删除修改对象,日积月累,内存就出现溢出的现象
*/
System.out.println(collections.size());
结果是:4
把
config.properties
内容改为
className=java.util.HashSet();
结果变为:2
(七)类加载器
工程做完后,通常把bin文件下的class文件打成jar包
1、实际项目中,很少用相对路径
例如:InputStream ips=ReflectTest2.class.getResourceAsStream("config.properties");//相对路径
相对路径相对于
在运行里 输入
java MyClass xx.file
xx.file文件是相对于当前工作目录去运算,这样不方便,
可以用绝对路径来解决
2、配置文件config.properties文件在哪里
getRealPath()方法:得到项目的绝对位置
例如:安装了 “金山词霸”得到 金山词霸的位置,放到c盘得到c,放到d盘得到d,配置文件放在金山词霸内部的,
用金山词霸的绝对位置拼上内部位置,就得到一个完整的。
比如window-preferences-general-open click ,首选项打勾或不打勾,然后ok按钮,存起来
InputStream ips=new FileInputStream("config.properties");
如果要存,一定要完整路径
3、得到资源文件的方式,最常用的是InputStream,但是他不能替代FileInputStream
每一个.class文件,在用的时候,都要加载到内存里来,起这个作用的叫类加载器
既然可以加载.class文件,也可以加载普通文件
类加载器会提供一个方法
a、只要运行ReflectTest2.class这个类,一定被类加载器加载了,由类加载器getClassLoader加载进来,
getClassLoader可以加载.class文件
也能加载普通文件,用getResourceAsStream()这个方法, 在classPath指定的目录下逐一的去查找要加载的文件
真正运行的时候是去classPath里去找,而不是源目录里去找
//InputStream ips=ReflectTest2.class.getClassLoader().getResourceAsStream("config.properties");//路径这么写是,去classPath下的根目录下找
//InputStream ips=ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");//相对路径,classPath下的路径里
框架的配置文件都是放在classPath指定的目录加载,都是用类加载器方式来加载文件,
b、配置文件在包里
class也提供了方法来加载资源文件,其实就是内部调用ClassLoader
InputStream ips=ReflectTest2.class.getResourceAsStream("config.properties");//相对路径,相对于我的包而言的;
c、配置文件在包下面子包里
InputStream ips=ReflectTest2.class.getResourceAsStream("resource/config.properties");//相对路径,相对于本包的子包
c、从根目录开始的路径
InputStream ips=ReflectTest2.class.getResourceAsStream("/cn/itcast/day1/resource/config.properties");
配置文件的加载往往用类加载器,框架都是这么加的,用框架时,把配置文件放到classPath目录下
如果是eclipse开发就放到sourse下,或sourse字目录下,会自动拷贝到classPath下面去
(八)内省IntroSpector
用于对JavaBean进行操作
JavaBean是特殊的Java类,主要用于传递信息,这种java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。
这种JavaBean的实力对象称之为值对象(Value Object,简称VO)
例:
int getAge()
void setAge(int age)
符合这种特定规则的java类(get,set打头)我们称之为javaBean
能把javaBean当作普通类来操作
普通类不见得能当作j'avaBean来操作
class Person{
private int x;//私有的,外面看不到
public int getAge(){
return x;//返回内部一个局部变量
}
public void setAge(int age){
this.x=age;//把传进来的参数给内部的那个变量
}
}
把Person当成JavaBean来看,他有一个什么名称的属性(是age,不是x,x外面看不到)
把Person当成JavaBean来操作,设置的属性是age,不能说设置x
JavaBean的属性是根据方法名称来的
有一个特点,去掉set和get后,就是属性名,
如果第二个字母是小的,则把再把首字母改成小的,否则不变
1、内省案例
用内省的方式读取JavaBean对象的x属性
package cn.test.day1;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
public class IntroSpectorTest {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
ReflectPoint pt1=new ReflectPoint(3,5);
String prpertyName="x";
/*
* PropertyDescriptor代表了javabean的属性
* getClass把类当成javabean来看
* javabean的属性由get和set得出来的,进一步就可以得到这个属性的get方法和set方法
*
*/
PropertyDescriptor pd=new PropertyDescriptor(prpertyName,pt1.getClass());
Method methodGetX=pd.getReadMethod();//得到x属性的读方法
Object retVal=methodGetX.invoke(pt1);//在pt1上调用invoke,得到一个返回值
System.out.println(retVal);
}
}
值为:3
取y的值
ReflectPoint pt1=new ReflectPoint(3,5);
String prpertyName="x";
/*
* PropertyDescriptor代表了javabean的属性
* getClass把类当成javabean来看
* javabean的属性由get和set得出来的,进一步就可以得到这个属性的get方法和set方法
* 要是用javabean的话,取值一定要用get取
*/
PropertyDescriptor pd=new PropertyDescriptor(prpertyName,pt1.getClass());
Method methodGetX=pd.getReadMethod();//得到x属性的读方法
Object retVal=methodGetX.invoke(pt1);//在pt1上调用invoke,得到一个返回值
System.out.println(retVal);
//把y值set进去
Method methodSetX=pd.getWriteMethod();
methodSetX.invoke(pt1, 7);//set接受参数,把参数改为7,7的位置应该为对象,java5自动装箱
System.out.println(pt1.getX());//y打印出来了
选中
PropertyDescriptor pd=new PropertyDescriptor(prpertyName,pt1.getClass());
Method methodGetX=pd.getReadMethod();//得到x属性的读方法
Object retVal=methodGetX.invoke(pt1);
右键->reflector->Extract Method...
方法的格式是某个方法接收两个参数,起名getProperty
方法就会生成
Object retVal = getProperty(pt1, prpertyName);
private static Object getProperty(Object pt1, String prpertyName)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
PropertyDescriptor pd=new PropertyDescriptor(prpertyName,pt1.getClass());
Method methodGetX=pd.getReadMethod();//得到x属性的读方法
Object retVal=methodGetX.invoke(pt1);
return retVal;
methodSetX.invoke(pt1, 7);
不是变量时没法重构,所以可以设成变量
Object value=7;//不为变量没法设
选中
PropertyDescriptor pd2=new PropertyDescriptor(prpertyName,pt1.getClass());
Method methodSetX=pd2.getWriteMethod();
methodSetX.invoke(pt1, value);
重构
Object value=7;//不为变量没法设
setProperties(pt1, prpertyName, value);
System.out.println(pt1.getX());
}
private static void setProperties(Object pt1, String prpertyName,
Object value) throws IntrospectionException,
IllegalAccessException, InvocationTargetException {
PropertyDescriptor pd2=new PropertyDescriptor(prpertyName,pt1.getClass());
Method methodSetX=pd2.getWriteMethod();
methodSetX.invoke(pt1, value);
}
2、内省的复杂案例
private static Object getProperty(Object pt1, String prpertyName)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
/*PropertyDescriptor pd=new PropertyDescriptor(prpertyName,pt1.getClass());
Method methodGetX=pd.getReadMethod();//得到x属性的读方法
Object retVal=methodGetX.invoke(pt1);*/
/*Introspector类中有getBeanInfo方法,
* getBeanInfo:把java类(pt1.getClass())当成javaBean来看;
* 看出来的结果用BeanInfo来表示,
* BeanInfo就代表javaBean细节信息,
* */
BeanInfo beanInfo=Introspector.getBeanInfo(pt1.getClass());//
PropertyDescriptor[] pds=beanInfo.getPropertyDescriptors();//所有的属性信息
Object retVal=null;
for(PropertyDescriptor pd:pds){
if(pd.getName().equals(prpertyName)){
Method methodGetX=pd.getReadMethod();//得到x属性的读方法
retVal=methodGetX.invoke(pt1);
break;
}
}
return retVal;
}
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
ReflectPoint pt1=new ReflectPoint(3,5);
String prpertyName="x";
/*
* PropertyDescriptor代表了javabean的属性
* getClass把类当成javabean来看
* javabean的属性由get和set得出来的,进一步就可以得到这个属性的get方法和set方法
* 要是用javabean的话,取值一定要用get取
*
* 把methodGetX属性抽成一个方法
*/
Object retVal = getProperty(pt1, prpertyName);//在pt1上调用invoke,得到一个返回值
System.out.println(retVal);
}