----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
1.反射的基石->Class 类
Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class。一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类得字节码,不同的类得字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型。
得到各个字节码对象的实例对象(Class 类型)有下面三种方法:
类名.class,例如,System.class
对象.getClass(),例如,new Date().getClass()
Class.forName("类名"),例如,Class.forName("java.util.Date");
例:String str = "abc";
Class cls1 = String.class;
Class cls2 = str.getClass();
Class cls3 = Class.forName("java.lang.String");
System.out.println(cls1); //代表一份字节码
System.out.println(cls2);
System.out.println(cls3);
打印的结果都为 class java.lang.String,
System.out.println(cls1.isPrimitive()); //是否是原始类型(boolean,byte,char,short,int,long,float,double)(String 是一个类,返回false)
System.out.println(int.class.isPrimitive());
System.out.println(int.class == Integer.class);
System.out.println(Integer.TYPE); //打印出Integer包装类的基本类型(int)
System.out.println(int.class == Integer.TYPE); //Integer.TYPE 代表的是包装的那个基本类型的字节码(和int.class 代表的字节码一样)
System.out.println(int[].class.isPrimitive());
System.out.println(int[].class.isArray()); //判断是否是数组类型的class实例对象
总之,只要在源程序中出现的类型,都有各自的class实例对象,例如,int[],void(void.class)...
2.反射
反射就是把Java类中的各种成分映射成相应的Java类。表示Java类得Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Constructor、Package等等。
2.1 Constructor类
Constructor类代表某个类中的一个构造方法。
得到某个类所有的构造方法:
Constructor[] constructors = Class.forName("java.lang.String").getConstrutors();
得到某一个构造方法:
Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class); //获得指定的构造函数(传入的参数为StringBuffer)
创建实例对象:
通常方式:String str = new String(new StringBuffer("abc"));
反射方式:String str = (String)constructor.newInstance(new StringBuffer("abc"));
//调用获得方法时要用到上面相同类型的实例对象
Class.newInstance() 方法:
例子:String obj = (String) Class.forName("java.lang.String").newInstance(); //该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象
2.2 Field 类
Field 类代表某个类中的一个成员变量。看下面的例子:
先创建一个类
public class ReflectPoint {
private int x;
public int y;
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
ReflectPoint pt1 = new ReflectPoint(3,5);
Field fieldY = pt1.getClass().getField("y");
System.out.println(fieldY.get(pt1)); //取出变量在某个对象上的值(结果为5)
注意:fieldY不是对象身上的变量,而是类上,要用它去取某个对象对应的值
/* Field fieldX = pt1.getClass().getField("x");
System.out.println(fieldX.get(pt1)); */
//这种方法只能获取public类型的变量,x 为private类型,所以用这种方法,达不到效果
Field fieldX = pt1.getClass().getDeclaredField("x"); //获取所有声明过的变量
fieldX.setAccessible(true); //如果不设置成可以访问的话,就算fieldX中有所有声明过的变量,但是你取不到fieldX里面的值(暴力反射)
System.out.println(fieldX.get(pt1));
通过上面这种方法,可以访问类中的私有类型(private) 的变量。
2.3 Method 类
Method代表某个类中的一个成员方法。
得到类中的某一个方法:
Method methodCharAt = String.class.getClass("CharAt",int.class);
调用方法:
通常方式:System.out.println(str.CharAt(1));
反射方式:System.out.println(CharAt.invoke(str,1));
如果传递给Method对象的invoke()方法的一个参数为null,说明该Method对象对应的是一个静态方法!
3.数组的反射
①具有相同维数和元素类型的数组属于同一类型,即有相同的Class实例对象。
②代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
③基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用,非基本类型的一维数组,既可以当做Object类型使用,又可以当作Object[]类型使用。
例:int[] a1 = new int[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()); //属于同一个字节码(返回为true)
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());
int a = new int[] {1,2,3}
System.out.println(Arrays.asList(a1)); //打印的结果是1,2,3(作用是依次打印出数组的每个元素)
4.反射的作用->实现框架功能
set中的数据对象没有顺序且不可以重复;List中的数据对象有顺序且可以重复。看下面的例子:
Collection collections = new ArrayList();
// Collection<ReflectPoint> collections = new HashSet<ReflectPoint>();
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()); //结果为4(List中的数据对象有顺序且可以重复)
System.out.println(collections.size()); //结果为3(Set中的数据对象没有顺序且不可以重复)
如果实现了下面的方法:
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ReflectPoint other = (ReflectPoint) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
public String toString() {
return str1 + ":" + str2 + ":" + str3;
}
打印的结果是2。(pt1 和 pt2 是同一个对象)
用反射和配置文件来管理
创建一个config.properties 文件,根据该文件路径的不同,有下面不同的写法:
// InputStream is = new FileInputStream("config.properties");
// InputStream is = ReflectTestAgain.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");
InputStream is = ReflectTestAgain.class.getResourceAsStream("config.properties"); //相对路径(与类在同一个包下)
Properties props = new Properties();
props.load(is);
is.close();
String className = props.getProperty("className");
Collection collections = (Collection) Class.forName(className).newInstance();
反射最主要的应用就是框架,在以后的学习中,会慢慢体会到……
----------------------- android培训、java培训、java学习型技术博客、期待与您交流! ----------------------