【看张老师的视频笔记和总结】
1、反射的基础–Class类
各个Java类属于同一个事物,描述这类事物的java类名就是Class。Class类代表java类,其各个实例对象分别对应个各类在内存中的字节码。
(1)字节码
一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码。
不同类的字节码是不一致的,其在内存中的内容也是不同的。这一个一个的空间分别用一个一个对象表示,这些对象具有相同的类型,这个类型就是Class。
(2)如何得到各个字节码对应的实例对象(Class类型)
* 类名.class , 如 System.class
* 对象.getClass() ,如 new Date().getClass()
* Class.forName(“类名”) , 如 Class.forName("java.util.Date")
public class ResflectTest {
public static void main(String[] args) throws Exception{
// 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(cls2==cls3);
System.out.println(cls1.isPrimitive()); //cls1的类型是基本类型吗
System.out.println(int.class.isPrimitive()); //字节码是否是基本类型
System.out.println(int.class==Integer.class);
//int字节码的类型与Integer字节码的类型
System.out.println(int.class == Integer.TYPE); //
System.out.println(int [].class.isPrimitive());
}
}
(3)九个预定义Class实例对象
数组属于一个Class类,所有具有相同元素类型和维数的数组共享一个Class对象。基本的java类型(primitive Java types)[int,short,long,float,double,boolean,char,byte]和void类型也表示为Class对象。
如,int.class ,Integer.class ,Integer.TYPE
数组类型的Class实例对象:int [].class ; Class.isArray
2、反射(Reflection)
java中反射是指,我们可以在运行时加载、探知、使用编译期间完全未知的类。也即,java程序可以加载一个运行时才得知名称的类,获悉其完整构造,并生成其对象实体、或对其变量设值、或调用其方法。这种“看透类”的能力被称为Introspection(内省、内观、反省)。JavaBean是Reflection的实际应用之一。
反射就是把java类中的各种成分映射成为相应的java类。
(1)构造方法的反射应用
Class对象可以获得该类里成分包括方法(由Method对象表示)、构造器(由Construction对象表示)、Field(由Field对象表示),程序可以通过Method对象来执行队形的方法,通过Construction对象来调用对应的构造器创建对象,通过Field对象直接访问并修改对象的属性值。
Construction类代表某个类中的一个构造方法
A、得到某类中所有的构造方法
Constructor [] constructors = Class.forName("java.lang.String").getConstrutors();
B、 得到某一个构造方法
Constructor constructor = Class.forName("java.lang.String").getConstrutors
(StringBuffer.class);//获得方法时要用到类型
C、创建实例对象
通常方式:String str = new String(new StringBuffer("abc"));
反射方式:
Constructor constructor = String.class.getConstructor(StringBuffer.class);
String str = (String)constructor.newInstance(new StringBuffer("abc"));
//调用获得的方法时,要用到上面相同类型的实例对象
D、Class.newInstance()方法
String obj = (String)Class.forName("java.lang.String").newInstance();
//该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
(2)成员变量的反射(Field类)
Field类代表某个字节码中的变量,不代表某个对象中的变量。即,Field对象对应的是类上面的成员变量,不是对象的的成员变量。如下示:
fieldY代表的是x的定义,不是具体的x变量。
private int x;
public int y;
ReflectPoint pt1 = new ReflectPoint(3,5);
Field fieldY = pt1.getClass().getField("y");
//getField()方法只能读取公有的成员变量。(即看不到除去公有的成员变量)
System.out.println(fieldY.get(pt1));
Field fieldX = pt1.getClass().getDeclaredField("x");
//getDeclaredField()方法可以读取任意的成员变量
fieldX.setAccessible(true);
/*因为x设置为私有的,虽然上步getDeclaredField("x")成功编译,
*但是在读取时出现private,即只能看见,但是使用不了。
*这时使用setAccessible(true);设置反射的对象可以读取。(暴力反射)
*/
System.out.println(fieldX.get(pt1));
例子:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的“b”改为“a”。
public static void main(String[] args) throws Exception{
ReflectPoint pt1 = new ReflectPoint(3,5);
changeStringValue(pt1);
System.out.println(pt1);
}
public static void changeStringValue(Object obj) throws Exception
{
Field[] field1 = obj.getClass().getFields();
for (Field field:field1)
{
if(field.getType() == String.class)
//同一份字节码,即是相同的内存地址的值,则使用==进行比较
{
String oldValue = (String)field.get(obj);
String newValue = oldValue.replace('b', 'a');
field.set(obj, newValue);
}
}
}
public class ReflectPoint {
private int x;
public int y; //定义为共有的,才能在ReflectTest中调用“y”
public String str1= "cat";
public String str2= "bobcat";
public String str3= "catball";
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
public String toString()
{
return str1 + ":" +str2 + ":" +str3;
}
}
(3)成员方法的反射
Method类:代表某个类中的一个成员方法。
(方法中的参数类型用Class对象表示)
如:Method charAt = Class.forName("java.lang.String").getMethod("charAt",int.class);
System.out.println(charAt.invoke(str,1));//反射方式
System.out.println(str.charAt(1));//通常方式
【如果传递给Method对象的invoke()方法的第一个参数是null,则说明该Method对象对应的是一个静态方法】
//成员方法的反射
Method methodcharAt = String.class.getMethod("charAt", int.class) ;
System.out.println(methodcharAt.invoke(str1, 1));
System.out.println(methodcharAt.invoke(str1, new Object[] {2}));
//int i[] = new int [] {1,2,3};
//String str[] = new String [] {new String("abc"),new String("123")};
//Object shuzu = new Object[] {new String("abc"),2};
[对接收数组参数的成员方法的反射]
通过反射的方式调用有数组参数的方法时(main方法),invoke方法怎样传递参数?
public Object invoke(Object obj,Object... args)
public Object invoke(Object obj,Object [] args)
可知,invoke方法需要Object类型的参数,整个数组作为一个参数。
即,invoke(Object obj,new Object [] {数组})。
/*静态代码的方式直接调用main方法
* TestArguments.main(new String[] {"123","abc"});
* class TestArguments{
public static void main(String [] args)
{
for(String arg:args)
{
System.out.println(arg);
}
}
}
*/
String startingClassName = args[0];
Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);
//mainMethod.invoke(null, new Object[]{new String[] {"123","abc"}});
//将new String[] {"123","abc"}作为Object数组的一个元素,这样符合invoke的构造方法参数传递。
mainMethod.invoke(null, (Object)new String[] {"123","abc"});
//invoke需要Object类型的数组参数,但是new String[] {"123","abc"}是字符串数组,所以进行强制类型转换即可。
(4)数组的反射
前边提及到,具有相同元素和维度数目的数组是一个Class。
数组与Object的关系与反射类型
Object是所有类的超类。
JAVA中只有基本类型不是对象,但是String、数组是对象,都是从Object类中扩展而来。
int [][]可以转化为Object[]是因为,int[]是Object对象,则int[][]转换为Object[]时,相当于其元素是int[]。
int[]不能强制转换成Object[],但是可以转换为Object类。是因为int是基本数据类型,不是对象,也即不是Object对象,int[]是基本数据类型int的一维数组,是由Object类扩展而来。
String是对象,也是Object类型的对象,则String[]可强转成Object[]
String [] a4 = new String[]{"a","b","c"};
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);
}
}
怎样得到数组中元素的类型?
【只能得到某个元素的具体类型:args[1].getClass().getName();例如Object[]整个数组的元素类型可以是{“123”,123}等。】
3、反射的作用(实现框架功能)
//Collection collection = new ArrayList();使用通常ArrayList
//使用反射达到ArrayList的功能,此处将类名保存为一个文件,这样不用在程序中修改类名。
InputStream ips= new FileInputStream("config.properties");
Properties pros = new Properties();
pros.load(ips);
ips.close();
String className = pros.getProperty("className");
Collection collections =(Collection) Class.forName(className).newInstance();
//Collection collections = new HashSet();要实现HashSet功能时,将文件中的类名改为HashSet即可
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());
这里的文件路径是相对路径,在实际中不是这样存放的。一种是设置为绝对路径;
一种是getRealPath();//得到总的目录在硬盘中对应的位置,最后拼接上自己内部的地址,就是文件的完整地址.InputStream ips= ReflectTest2.class.getResourceAsStream("resouces/config.properties");