------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
反射的概念
反射就是把java类中的各种成分映射成相应的java类。
一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示。
反射的基础:Class-->用来描述java中的类
如何得到各个字节码对应的实例对象
->类名.class 或者 类名.TYPE
->对象.getClass()
->Class.forName("类名")
总之,只要在源程序中出现的类型,都有各自的Class实例对象。
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);
运行结果:true true
注意:
1. int.class = integer.TYPE
2. 数组类型的class实例对象Class.isArray()为true
3. 反射不是java5的新特性
反射的作用
反射机制最重要的内容——检查类的结构。
在java.lang.reflect包中有三个类Field、Method、Constructor分别用于描述类的域、方法和构造器。
这三个类共有方法:
getModifiers //返回一个整形数值,用不同的位开关描述public和static这样的修饰符使用状况
getName //用来返回项目的名称
构造方法的反射应用
constructor代表一个构造方法,constructor对象上的方法有:得到方法名字,得到所属类,产生实例对象。
|--得到无参构造函数:Constructor[] constructors = Class.forName("java.lang.String").getConstructors();
|--得到有参构造函数:Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
创建实例对象
|--通常方式: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(); //该方法内部先得到默认构造方法,然后该构造方法创建实例对象。
成员变量的反射
Field类代表某个类中的一个成员变量
示例:通过Field调用成员变量
ReflectPiont.java
package com.itheima.reflect;
public class ReflectPoint {
private int x;
public int y ;
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
ReflectTest.java
package com.itheima.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class ReflectTest {
public static void main(String[] args) throws Exception {
ReflectPoint rp = new ReflectPoint(3, 5);
Field fieldY = rp.getClass().getField( "y");
System. out.println(fieldY.get(rp));
//结果:5
}
}
注意:如果想直接通过getField方法获取私有对象,会出现如下错误
Exception in thread "main" java.lang.NoSuchFieldException : x
解决方法:
Field fieldX = rp.getClass().getDeclaredField("x");
fieldX.setAccessible(true);
这种方法称之为暴力反射,使用setAccessible(true)使private类型的成员变量也可以被获取。
成员方法的反射
Method类用于描述类中的成员方法。
得到类中的某一个方法:
Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
调用方法:
|--通常方法:System.out.println(str.charAt(1));
|--反射方法:System.out.println(charAt.invoke(str, 1));
注意:如果传递给Method对象的invoke()方法的第一个参数为null,说明该Method对象对应的是静态方法!
数组与Object的关系及其反射类型
1. 具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
package com.itheima.reflect;
import java.util.Arrays;
public class ReflectTest {
public static void main(String[] args) throws Exception {
int [] 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());
//结果:false
System.out.println(a1.getClass() == a3.getClass());
//结果:false
}
}
2. 代表数组的class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
3. 基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,即可以做Object类型使用,又可以当作Object[]类型使用。
4. Array.asList()方法处理int[]和String[]时的差异
public class ReflectTest {
public static void main(String[] args) throws Exception {
int [] a5 = new int[]{1,2,3};
String[] a6 = new String[]{"a" ,"b" ,"c" };
//直接使用System.out.println无法打印出数组的内容
System. out .println(a5);
//结果:[I@18a992f
System. out .println(a6);
//结果:[Ljava.lang.String;@4f1d0d
//通过Arrays.asList方法打印出数组的内容
System. out .println(Arrays.asList(a5));
//结果:[[I@18a992f]
//原因是因为JDK1.4中为Arrays.asList(Object[] a),JDK1.5中为Arrays.asList(T... a)。
//a5是int[]类型,JDK1.4中的asList方法处理不了,JDK1.5可以处理。但是JDK1.5将 int数组整体作为一个参数进行处理。
//因此最终结果就是将 int[]进行了封装,结果类型也就成了[[I。
System. out .println(Arrays.asList(a6));
//结果:[a, b, c]
}
}
5. Array工具类用于完成数组的反射操作。
public class ReflectTest {
public static void main(String[] args) throws Exception {
String[] a1 = new String[]{"a" ,"b" ,"c" };
String a2 = "xyz";
printObject(a1);
printObject(a2);
}
public static void printObject(Object obj){
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);
}
}
}
6. ArrayList和HashSet的比较及Hashcode分析。
ArrayList:
Collection collections = new ArrayList();
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 Collection collections = new ArrayList();
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
HashSet:
Collection collections = new 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());
//结果:3
分析:
当集合为HashSet时,需要通过比较hashcode值以及equals方法是否返回true决定是否放入。如果hashcode值相等并且equals方法返回true,那么就不会放入。因此,集合size为3。
hashCode()和equals()覆盖:
@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 ;
}
此时,运行ReflectTest.java结果为2。
注意:
当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露。
框架的概念及用反射技术开发框架的原理
1. 框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。
2. 框架程序怎样调用以后写的类呢?很多时候程序无法知道被调用的类名,所以,在程序中无法直接new某个类的实例对象,而要用反射方式来做。
ReflectTest.java
public class ReflectTest {
public static void main(String[] args) throws Exception {
InputStream is = new FileInputStream("config.properties" );
Properties props = new Properties();
props.load(is);
is.close();
String className = (String)props.get( "className");
Collection collections = (Collection)Class.forName(className).newInstance();
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());
}
}
config.properties文件直接放在根目录下:
|--config.properties
className = java.util.ArrayList //结果:4
|--config.properties
className = java.util.HashSet //结果:3
用类加载器的方式管理资源和配置文件
//方式一:采用类加载器进行加载,使用相对路径的方式
//InputStream is = ReflectTest.class.getClassLoader().getResourceAsStream("com/itheima/day1/config.properties");
//方式二:利用Class方式进行加载,使用相对路径的方式
//InputStream is = ReflectTest.class.getResourceAsStream("config.properties");
//方式三:利用Class方式进行加载,使用绝对路径的方式
InputStream is = ReflectTest.class .getResourceAsStream("/com/itheima/day1/config.properties");