------- android培训、java培训、期待与您交流! ----------
Class类:类的类型,如String类的类型就是String.class,代表类在内存中的一份字节码
获取字节码的三种方法:
类名.class,如Stirng.class
创建一个对象,如Person p=new Person(); p.getClass()
使用Class类中的静态方法:Class.forName("完整类名"),如Class.forName("java.util.Date")
九种预定义的Class实例对象:八种基本数据类型都有字节码:boolean,byte,char,short,int ,long,float,double,还有返回值类型void
import java.lang.reflect.*;
import static java.lang.System.*; //静态导入包,方便打印
class Reflect
{
public static void main(String[] args) throws Exception
{
//获取字节码的三种方法
String str="abc"; //初始化一个字符串
Class cla1=str.getClass(); //通过对象获取类的字节码
Class cla2=String.class; //类名.calss 返回字节码
Class cla3=Class.forName("java.lang.String"); //使用静态方法forName()方法,并将类所在包传入获取字节码
System.out.println(cla1==cla2);
System.out.println(cla1==cla3);
//打印结果显示三种方法都可以返回String类的字节码
//判定指定的Class对象是否是基本类型
System.out.println(cla1.isPrimitive()); //判断是否是原始类型,结果是false,String是一个实例对象,所以不是原始类型
System.out.println(int.class.isPrimitive()); //结果正确,因为int是最原始最基本的数据类型
System.out.println(int.class==Integer.class);
out.println(int.class==Integer.TYPE); //结果正确,TYPE代表基本类型包装的字节码
out.println(int[].class isPrimitive()); //结果错误,不是原始类型
out.println(int[].class.isArray()); //结果正确,数组类型的字节码类型就是Array
}
}
总之,只要是在源程序中出现的类型,都有各自的Class实例对象,如int[]
反射:反射就是把java中的各种成分映射成相应的java类,如成员变量,方法,构造方法,修饰符,包等信息也用一个个的java类来表示,获取这些成分的Class类用各自的实例对象来表示:Field,Method,Contructor,Package等
1.构造方法的反射
例子,得到StringBuffer的构造方法
import java.lang.*;
import java.lang.reflect.*;
class ReflectDemo4
{
public static void main(String[] args) throws Exception
{
//new String(new StringBuffer("abc"));
Constructor con=String.class.getConstructor(StringBuffer.class); //通过此方法,获取Class对象即StringBuffer的构造方法
String str=(String)con.newInstance(new StringBuffer("abc")); //创建该构造方法的声明类的新实例,并初始化该实例
//注意此处用到两个StringBuffer,第一个说明调用StringBuffer的构造方法,第二个是用此构造方法时,还得传入一个StringBuffer对象进去
System.out.println(str.charAt(1)); //调用构造方法底层对应方法获取1号位的字符
}
}
2.成员变量的反射
先创建一个反射点的类函数
public class ReflectPoint
{
private int x;
public int y;
public ReflectPoint(int x,int y)
{
this.x=x;
this.y=y;
}
}
使用到了字段对象Field
import java.lang.reflect.*;
import java.lang.*;
import static java.lang.System.*; //静态导入包,方便打印
class ReflectDemo
{
public static void main(String[] args) throws Exception
{
ReflectPoint rp=new ReflectPoint(3,6); //构造反射点对象,
Field Y=rp.getClass().getField("y"); //获取反射实例对象的“y”字段,即成员变量y
out.println(Y.get(rp)); //获取y的值
Field X=rp.getClass().getDeclaredField("x"); //加上Declared:使私有成员变量x可见
X.setAccessible(true); //允许获取x的值,暴力反射
out.println(X.get(rp));
}
}
练习:将字符串中的'b'改为‘a’
建立反射点:
public class ReflectPoint
{
//声明三个变量
public String str1="bascaketball";
public String str2="back";
public String str3="catch";
public String toString() //复写String中的toString方法
{
return str1+"--"+str2+"--"+str3;
}
}
接下来实现该程序
import java.lang.reflect.*;
import java.lang.*;
class ReflectTest1
{
public static void main(String[] args) throws Exception
{
ReflectPoint rp=new ReflectPoint(); //构造反射点对象
FillValue(rp);
System.out.println(rp);
}
private static void FillValue(Object obj) throws Exception
{
Field[] fields=obj.getClass().getFields(); //获取字段数组,元素是获取到的字段
for(Field f:fields)
{
if(f.getType()==String.class) //因为使用的是同一份String字节码,所以使用等于号
{
String s1=(String)f.get(obj); //获取每一个字符串
String s2=s1.replace('b','a'); //将字符串中的b换成a
f.set(obj,s2); //通过Field中的set方法将替换后的字符串返回
}
}
}
}
3.成员方法的反射
类名.class.getMethod(”调用方法名“,调用的方法的参数字节码类型) //获取方法的字节码
invoke(对象,对象方法参数),使用Method类的此方法调用底层对应的方法
import java.lang.String.*;
import java.lang.reflect.*;
class ReflectDemo1
{
public static void main(String[] args) throws Exception
{
String str="abc"; //创建一个对象
//使用反射实现str.charAt(1)
Method meCharAt=String.class.getMethod("charAt",int.class); //获取charAt方法,此方法接受的参数是角标,属于Int.class类型
System.out.println(meCharAt.invoke(null,1)); //调用Method 对象表示的底层charAt()方法
}
}
如果invoke()调用的方法是静态的,则对象可以为空、
练习:对数组参数的成员方法进行反射,
import java.lang.reflect.*;
class ReflectDemo2
{
public static void main(String[] args) throws Exception
{
//TestArguements.main(new String[]{"11","22","33"});通过反射调用main方法
String myClassName=args[0]; //获取即将要调用的第一个类
Method MainMethod=Class.forName(myClassName).getMethod("main",String[].class); //获取成员方法
MainMethod.invoke(null,new Object[]{new String[]{"11","22","33"}}); //调用成员方法的在字节码底层的方法,并将字符串数组作为Object数组的一个元素传入,相当于封包
}
}
class TestArguements
{
public static void main(String[] args)
{
for(String arg:args)
{
System.out.println(arg);
}
}
}
注意在运行时,还得传递参数路径:在dos命令行里默认的就是当前路径,所以只要在参数上加上类名就好,如此便可以将类作为参数传入主函数中
d:\java0217\reflect\javac ReflectDemo2 TestArguements
4.数组的反射:
import java.lang.reflect.*;
class ReflectDemo3
{
public static void main(String[] args)
{
//新建几种不同的基本类型数组
int[] a1=new int[]{1,2,3};
int[] a2=new int[3];
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().getName()); //结果是[I,表示int型的字节码
//获取父类的字节码名,结果都是Object
System.out.println(a1.getClass().getSuperclass().getName());
System.out.println(a4.getClass().getSuperclass().getName());
printObject(a1);
}
private static void printObject(Object obj) //创建打印方法
{
Class cl=obj.getClass(); //创建类类型对象
if(cl.isArray()) //判断是否数组类型
{
int len=Array.getLength(obj);//Array方法是属于java.lang.reflect中的,凭借Array中的各种方法可以操作数组
for(int x=0;x<len;x++)
{
System.out.println(Array.get(obj,x));//get(Object,index)返回指定数组对象中索引组件的值
}
}
}
}
对于以上成员变量,用对象接收
Object obj1=a1; //将一维数组封装为一个对象
Object obj2=a4; //将字符串数组封装为一个对象
//Object[] obj3=a1; //注意,int[]是基本类型的数组,不属于Objct[],所以此处不能接收int数组中的元素,这句话通不过
printObject(a1);
练习:建立集合,向集合中存入反射点对象
import java.lang.*;
import java.lang.reflect.*;
import java.util.*;
class ReflectTest2
{
public static void main(String[] args)
{
Collection c=new HashSet(); //新建一个集合
//创建反射点对象,并传入参数
ReflectPoint1 rp1=new ReflectPoint1(1,2);
ReflectPoint1 rp2=new ReflectPoint1(3,4);
ReflectPoint1 rp3=new ReflectPoint1(1,2);
//将反射点中得到的元素添加到集合中
c.add(rp1);
c.add(rp2);
c.add(rp3);
rp1.y=7; //修改y的值之后,删除时就找不到原来的rp1,所以无法删除,此时就会出现内存泄露
c.remove(rp1); //删除元素
System.out.println(c.size());
}
}
打印的结果是2
注意的是,修改了y的值之后,删除时就找不到原来的rp1所以无法删除。此处就引出了内存泄露,注意,这是hashCode里的特点,
就是说使用hashCode可能会发生内存泄露
反射的作用:实现框架功能
注意:框架和锁都是已经在内存中写好了的,区别是:使用框架,是使用框架调用所写的类,而锁则是工具类,类可以直接调用锁,简而言之,就是框架调用你,而锁是你调用它
练习:构造一个框架,可以调用框架中的类
先创建一个配置文件:conflag.properties;内容编辑为:className=java.util.ArrayList
import java.lang.*;
import java.lang.reflect.*;
import java.util.*;
import java.io.*;
class ReflectTest3
{
public static void main(String[] args) throws Exception
{
FileInputStream fis=new FileInputStream("config.properties"); //创建文件读取流,能够读取配置文件中的内容
Properties pp=new Properties(); //新建配置文件对象
pp.load(fis); //将读取流中的文件加载进配置文件对象中
fis.close(); //此处因为对象关联的系统资源没有被释放,所以会有内存泄露
String className=pp.getProperty("className"); //根据键获取配置文件中对应的值,即框架中的类
Class cl=Class.forName(className); //得到框架中的类之后,再获取其字节码
Collection c=(Collection)cl.newInstance(); //创建此class对象所表示的类的一个新实例,即为ArrayList.class的新实例ArrayList
//Collection c=(Collection)Class.forName(className).newInstance(); //缩写以上两句话
//创建反射点对象,并传入参数
ReflectPoint1 rp1=new ReflectPoint1(1,2);
ReflectPoint1 rp2=new ReflectPoint1(3,4);
ReflectPoint1 rp3=new ReflectPoint1(1,2);
//将反射点中得到的元素添加到集合中
c.add(rp1);
c.add(rp2);
c.add(rp3);
System.out.println(c.size());
}
}
以上就使用了一个小框架,可以通过反射调用ArrayList类