一、反射的基础:Class
1. 概述
java用于描述一类事物的共性,该类事物有什么属性,没有什么属性,java程序中的各个java类,他们是否属于同一类事物?这个类的的名称就是Class。
2. Class类:
1. Class加载一个class,虚拟机会在内存里生成一个字节码,通过该字节码可以创建多个对象。
2.格式:
//Class cls1 = Person.class;
//Class cls2 = p1.getClass();
//Class cls3 = Class.forName("java.lang.String");//查询,加载字节码
3. 三种方式获取到的字节码是相等的
4. 只要在源程序中出现的数据类型,都有各自的Class实例对象。例如:int[]和void
*/
public static void main(String[] args) throws Exception{
String str1 = "st1";
Class<? extends String> cls1 = str1.getClass();
Class<String> cls2 = String.class;
Class<?> cls3 = Class.forName("java.lang.String");
System.out.println("cls1="+cls1+"\tcls2="+cls2+"\t比较结果:"+(cls1==cls2));
System.out.println("cls1="+cls1+"\tcls3="+cls3+"\t比较结果:"+(cls1==cls3));
System.out.println(cls1.isPrimitive());
System.out.println(int.class.isPrimitive());
System.out.println(int.class == Integer.TYPE);// 基本类型的字节码和它对应的对象的TYPE是相等的。 即int.class == Integer.TYPE;
System.out.println(int[].class.isPrimitive());
System.out.println(int[].class.isArray());
Class<Void> clsv = void.class;
System.out.println(clsv);
}
二、 反射:
* 1. 概述:
* 反射就是把java类中的各种成分映射成对应的java类。
* 2. 构造方法的反射应用
* Constructor类代表某个类中的一个构造方法
* 得到某个类所有构造方法:
* eg:Constructor[] constructors= Class.forName("java.lang.String").getConstructor();
* 得到某一个构造方法:(获得方法时要用到类型)
* eg: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"));
public static void MethodDemo() throws NoSuchMethodException,
InstantiationException, IllegalAccessException,
InvocationTargetException { //构造方法的反射
//new String();
Constructor<String> constructor = String.class.getConstructor(StringBuffer.class); //jdk1.5新特性,可变参数
String str = constructor.newInstance(new StringBuffer("abc"));
System.out.println(str);
}
* 3. 成员变量的反射应用
* 详情见例子:
public static void FieldDemo() throws NoSuchFieldException,
IllegalAccessException {
Person person = new Person("ss","basketball",5);
//Field personname = person.getClass().getField("name"); //getField只能访问共有的成员
Field personname = person.getClass().getDeclaredField("name"); //getDeclaredField 无论是否公开都能看到
personname.setAccessible(true); //setAccessible:是否允许我强行获取
//fieldname的值是ss吗?不是!!!!
System.out.println(personname.get(person));
}
* 4. 成员变量案例
private static void changeString1(Object obj) throws Exception{
Field filed = obj.getClass().getDeclaredField("hobby");
filed.setAccessible(true);
if(filed.getType()==String.class){ //这里用"==",因为同一个字节码用==比较准确
String oldValue = (String)filed.get(obj);
String newValue = oldValue.replace('u', 'a');
filed.set(obj, newValue);
}
}
private static void changeString(Object obj) throws Exception{
Field[] filed = obj.getClass().getFields(); //获取obj内所有的成员变量
for(Field fil : filed){
if(fil.getType()==String.class){ //这里用"==",因为同一个字节码用==比较准确
String oldValue = (String)fil.get(obj);
String newValue = oldValue.replace('a', 'b');
fil.set(obj, newValue);
}
}
}
三、Method类:
1. Method基础应用
* Method类代表某个类中的一个成员方法
* 得到类中的某一个方法:
* eg:Method charAt = Class.forName("java.lang.String").getMethod("charAt",int.class);
* 调用方式:System.out.println(str.charAt(1));
* 反射方式:System.out.ptintln(charAt.invoke(str,1));
public static void main(String[] args)throws Exception {
//str.charAt(1);
String str = "It's my class";
//普通调用
System.out.println(str.charAt(1));
//反射调用
Method methodCharAt = String.class.getMethod("charAt",int.class);
System.out.println(methodCharAt.invoke(str,1));
System.out.println(methodCharAt.invoke(str,new Object[]{2}));//1.4特性 Object invoke(Object obj, Object[])
}
2.main方法的反射调用
为什么要用反射,因为我不知道这个main()方法是属于谁的 ,所以用反射
*
* 需求:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法
* 注意:main()方法的参数是一个字符串数组,而invoke()方法版本中,会将后面的数组当作参数打散。
* 所以不能使用mainMethod.invoke(null,new String[]{"xxxx"});
* 解决办法:
1. mainMethod.invoke(null,new Object[]{new String[]{"xxxx"}})
2. mainMethod.invoke(null,(Object)new String[]{"cccc"});
class TestArguments{
public static void main(String[] args){
for(String arg : args){
System.out.println(arg);
}
}
}
public class MainTest {
/**
* @author Mr.z
*
* 为什么要用反射,因为我不知道这个main()方法是属于谁的 ,所以用反射
*
* 需求:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法
*
*
* 注意:main()方法的参数是一个字符串数组,而invoke()方法版本中,会将后面的数组当作参数打散。
* 所以不能使用mainMethod.invoke(null,new String[]{"xxxx"});
* 解决办法: 1. mainMethod.invoke(null,new Object[]{new String[]{"xxxx"}})
* 2. mainMethod.invoke(null,(Object)new String[]{"cccc"});
*/
public static void main(String[] args)throws Exception {
//静态方式调用main方法
TestArguments.main(new String[]{"111","222"});
//反射方式调用
String startingClassName = args[0];
Method mainMethod = Class.forName(startingClassName).getMethod("main",String[].class);
//mainMethod.invoke(null, new Object[]{new String[]{"123"}});
mainMethod.invoke(null,(Object)new String[]{"123"});
}
}
四、数组反射
1、反射类型:
* 1. 具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
* 2. 代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class
* 3. 基本类型的一维数组可以被当作Object使用,不能作为Object[]使用。非基本类型的一维数组,既
* 可以被当作Object使用,又能作为Object[]使用
* 4. Arrays.asList()方法处理int[]和String[]时候,不能直接用来处理int[]
2、数组反射的应用
import java.lang.reflect.Array;
import java.util.Arrays;
public class ArrayFelection {
/**
* @author Mr.zz
*/
public static void main(String[] args) throws Exception {
//ArrayTest();
String [] str = {"aaaa","bbbb","cccc"};
Object obj = str;
printObject(obj);
printObject("asdfsdfsad");
}
private static void printObject(Object obj) {
Class<?> cla = obj.getClass(); //获取字节码文件
if (cla.isArray()) {
int lang = Array.getLength(obj);
for (int i = 0; i < lang; i++) {
System.out.println(Array.get(obj,i));
}
} else {
System.out.println(obj);
}
}
public static void ArrayTest() {
int[] a1 = new int[] { 4, 5, 6 };
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());
Class<?> c1 = a1.getClass();
Class<?> c3 = a3.getClass();
Class<?> c4 = a4.getClass();
System.out.println(c1 == c3);
System.out.println(c1 == c4);
System.out.println(a1.getClass().getName());
System.out.println(a1.getClass().getSuperclass().getName());
System.out.println(a4.getClass().getSuperclass().getName());
// Object obj1 = a1;
// Object obj2 = a2;
// Object[] objError = a1; //这是错误的,因为基本数据类型不是对象
// Object[] obj3 = a3;
// Object[] obj4 = a4;
System.out.println(a1);
System.out.println(a4);
System.out.println(Arrays.asList(a1)); // 会报错,因为int不是对象
System.out.println(Arrays.asList(a4));
}
五、反射的综合应用:制作框架
代码:
import com.itema.bean.ReflectPoint;
public class ReflectDemo1 {
/**
* @author Mr.zz
* 反射的综合应用:制作框架
*
*/
public static void main(String[] args) throws Exception {
//HashSetDemo_Leak();
InputStream ips = new FileInputStream("config.properties");
Properties props = new Properties(); //等效于hashMap,但是它可以从文件中读取大量元素,而不必一个一个读取
//从文件中读取信息
props.load(ips);
//良好的习惯:用完就关闭,避免内存泄漏
ips.close();
String className = props.getProperty("className1");
@SuppressWarnings("unchecked")
Collection<Object> collections = (Collection<Object>)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);
//pt1.y = 7;
collections.remove(pt1); //因为pt1的hash值在存入的时候通过计算,将pt1存在了相应的区域,然后我们改变流y的值,
//此时pt1的hash值已经改变,此时在对应得内存hash值区域并不能找到pt1,所以此时内存发生了泄露
System.out.println(collections.size());
}
注意:要在工程目录下新建一个名为config.properties的配置文件,并输入以下代码。
所谓框架就是被别人调用的。所以用反射调用配置文件里的信息可以方便别人修改
className1 = java.util.ArrayList
className1 = java.util.HashSet