一、反射的基础
java程序中各个java类属于同一类事物,描述这类事物的java类名就是Class
比如说,很多人,用java来表示就用Person类,很多类,就用Class,Person类的实例对象比如张三、李四代表着一个个具体的人,而Class类就代表着各个类在内存中的字节码
一个类被类加载器加载进内存,会占用一片存储空间,这个空间的内容就是类的字节码,不同的类的字节码不同,所以他们在内存中的内容是不同,这些空间分别用一个一个对象来表示,这些对象具有相同的类型,这个类型就是Class
面试题:Class.forName("java.lang.String"),forName()的作用:
返回字节码,返回的方式有两种:1.这份字节码曾经被加载过,已经存在jvm中,那么就可以直接返回。2.jvm中没有,用类加载器去加载,加载后,字节码缓存到jvm中,以后再这份字节码就不需要加载了。
得到一个类的字节码有三种方法:
1.String.class 2.Person p = new Person(); p.getClass();对象.class 3.Class.forName("java.lang.String");
了解:一共有9个预定义Class对象,8个基本类型+void.class
数组类型的class实例对象,int[].class.isArray();int数组的字节码是数组
总之:只要是在源程序中出现的类型都有各自的class实例对象,int[]、void
演示:
<span style="white-space:pre"> </span>public static void main(String[] args)throws Exception { String str1 = "abc"; Class class1 = str1.getClass(); Class class2 = String.class; Class class3 = Class.forName("java.lang.String"); System.out.println(class1==class2); System.out.println(class2==class3); System.out.println(class1.isPrimitive());//是否是原始类型,false,它是类 System.out.println(int.class.isPrimitive());//true System.out.println(int.class==Integer.class);//false System.out.println(int.class==Integer.TYPE);//true System.out.println(int[].class.isArray());//true }
二、理解反射的概念
一位大牛总结的一句经典:反射就是把Java类中的各种成分映射成相应的Java类。
一个java类中用一个Class类的对象来表示,一个类的组成成分:构造函数,方法,成员变量,包等也同java类来表示,车本身是一个类,它的轮胎、发动机等等也是一个个类。java的Class类提供一系列方法来获得构造函数、方法、成员变量等信息,这些信息就是相应类的实例对象,Field、Method、Contructor、Package等。
一个类中的每个成员都有可以用相应的反射API类的一个实例对象来表示
System.exit(0);
System.gc();
System是一个类
exit、gc也是一个类
Method method1 代表exit()
method2 代表gc()
他们都属于Method类
这也就可以很好的理解java的核心思想:万物皆对象。不论是什么,都是对象
三、反射的应用
1.构造方法
Constructor类代表某个类中的一个构造方法
得到类的所有构造方法: Constructor[] constructors = Class.forName("java.lang.String").getConstructors();
得到类的一个构造方法: Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
也可以是:Constructor constructor = String.class.getConstructor(StringBuffer.class);
在获得实例对象的时候要注意,指定实例对象的类型,因为编译器只知道是个对象,但是不知道是什么类型的对象String str = (String) constructor.newInstance(new StringBuffer("abc"));
2.成员变量
Field类代表某个类中的一个成员变量
注意fiedx代表的是x的定义,而不是具体的x变量,要想得到x的变量,就必须从对象上获取
public class Reflection { private int x; public int y; public Reflection(int x, int y) { super(); this.x = x; this.y = y; } } import java.lang.reflect.Field; public class LreanReflect { public static void main(String[] args)throws Exception{ Reflection re = new Reflection(3, 4); Field fieldy = re.getClass().getField("y"); //fieldy是多少?4?错,fieldy不是对象身上的变量,而是类上,要用它取某个对象上的值 int st = (Integer)fieldy.get(re); System.out.println(st); //x是私有的 Field fieldx = re.getClass().getDeclaredField("x");//允许你看见,但是不让你用 fieldx.setAccessible(true);//暴力反射,不管它同不同意,我都要用 st = (Integer)fieldx.get(re); System.out.println(st); } }
成员变量反射的综合案例
将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"
import java.lang.reflect.Field; public class LreanReflect { public static void main(String[] args) throws Exception { Reflection reflection = new Reflection(3, 4); ChangeStringValue(reflection); System.out.println(reflection); } private static void ChangeStringValue(Object obj) throws Exception{ Field[] fields = obj.getClass().getFields(); for(Field f : fields){ //if(f.getType().equals(String.class))//因为只有一份String的字节码 if(f.getType()==String.class){ String str = (String)f.get(obj); //String value = str.replaceAll("b", "a"); String value = str.replace('b', 'a'); f.set(obj,value);//将更改后的值更新给Reflection } } } } package ReflectLearn; public class Reflection { private int x; public int y; public String str1 = "ball"; public String str2 = "basketball"; public String str3 = "mail"; public Reflection(int x, int y) { super(); this.x = x; this.y = y; } @Override public String toString(){ return str1+":"+str2+":"+str3; } }
3.成员方法
(1)调用方法
Method method = String.class.getMethod("charAt", int.class); String str1 = "abc"; System.out.println(method.invoke(str1, 1));//invoke,调用 //如果xxx.invoke(null,1)第一参数是null,那么这个方法肯定是静态的,因为不需要对象
(2)对接收数组参数的成员方法进行反射
写一个程序,这个程序能够根据用户提供的类名,取执行该类中的main方法
import java.lang.reflect.Method; public class LreanReflect { public static void main(String[] args) throws Exception { //MyMain.main(new String[]{"11","22","33"}); String startingClassName = args[0];//这里需要设置一下main参数,因为我不知道要运行那个main方法 //run as ->run Configurations->Arguments(ReflectLearn.MyMain) System.out.println(startingClassName); Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class); mainMethod.invoke(null, new Object[]{new String[]{"11","22","33"}});//打包 //mainMethod.invoke(null, (Object)new String[]{"11","22","33"}); //为什么要打包?main方法接受一个参数(字符串数组),我们传递给它一个字符串数组 //在JDK1.5一个数组就是对应一个参数,但是在JDK1.4,数组中的每一个元素对应一个参数 //我们传递一个String[],也就是一个Object数组,一打开,其中的每个元素都是一个参数,它就会认为收到3各参数 //所以,用Object数组,再打一个包,这样,我给你一包东西,打开后,是一个数组,这样就OK了 //正式因为,要拆开,所以我提前就给你打一个包 } } class MyMain{ public static void main(String[] args){ for(String s : args){ System.out.println(s); } } }
(3)数组的反射
一个数组也是一个Object,每一个相同元素类型,以及相同维度的数组,都是同一个class
import java.lang.reflect.Array; import java.util.Arrays; public class LreanReflect { public static void main(String[] args) throws Exception { int[] arr1 = new int[3]; int[] arr2 = new int[4]; int[][] arr3 = new int[3][4]; String[] arr4 = new String[]{"a","b","c"}; System.out.println(arr1.getClass() == arr2.getClass()); System.out.println(arr1.getClass().getName()); System.out.println(arr1.getClass().getSuperclass().getName()); Object obj1 = arr1; //Object[] obj2 = arr2;这样不行 Object[] obj3 = arr3; Object obj4 = arr4; Object[] obj5 = arr4; System.out.println(Arrays.asList(arr1)); System.out.println(Arrays.asList(arr4));//字符串可以 printElement(arr1); } private static void printElement(Object arr1) { Class c = arr1.getClass(); if(c.isArray()){ int len = Array.getLength(arr1); for(int i = 0;i<len;i++){ System.out.println(Array.get(arr1,i)); } }else{ System.out.println(arr1); } } }
延伸:关于hashCode
当一个对象被存储进HashSet集合中,就不能修改这个对象中那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HasgSet集合中时的哈希值不同,这就导致了使用当前集合对象的contains取检查修改后的对象,发生检索失败的现象,从而导致了无法从HashSet集合中删除这个修改前的对象,进而致使内存泄露
public class Reflection { private int x; public int y; @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; Reflection other = (Reflection) obj; if (x != other.x) return false; if (y != other.y) return false; return true; } public Reflection(int x, int y) { super(); this.x = x; this.y = y; } } public class ReflectTest2 { public static void main(String[] args) { Collection conCollection = new HashSet(); Reflection[] rs = new Reflection[]{ new Reflection(3, 3),new Reflection(4, 4),new Reflection(3, 3) }; for(Reflection r : rs) conCollection.add(r); //rs[0].y = 1; System.out.println(conCollection.contains(rs[0])); boolean b = conCollection.remove(rs[0]); System.out.println(b); System.out.println(conCollection.size()); } } true true 1 把注释打开 false false 2
所以说,如果我们往集合中存对象,又改对象,再删对象,对象并没有被删除,以此日积月累,内存泄露。所以存进集中的对象,就不要
修改了
四、框架的概念及用反射技术开发框架的原理
反射的作用->实现框架
模拟一个超小型框架
config.properties className=java.util.ArrayList public class Reflection { private int x; public int y; public Reflection(int x, int y) { super(); this.x = x; this.y = y; } } public class ReflectTest2 { public static void main(String[] args) throws Exception { InputStream in = new FileInputStream("D:/JavaSourue/config.properties"); Properties pro = new Properties();//从配置文件中,得到是什么类 pro.load(in); in.close(); String className = pro.getProperty("className"); Collection conCollection = (Collection)Class.forName(className).newInstance(); Reflection[] rs = new Reflection[]{ new Reflection(3, 3),new Reflection(4, 4),new Reflection(3, 3) }; for(Reflection r : rs) conCollection.add(r); System.out.println(conCollection.size()); } }
框架:
一个毛坯房就是一个框架,而用户自己装修门、窗等,用户使用的是框架,门和窗插入到框架中
框架和工具类的区别就是:工具类被用户所调用,而框架调用用户所提供的类
框架解决的问题:
房地产商在盖房子时(做框架),可能用户还没有买房,自然也就不会想到装修,那么框架怎么调用用户以后写的类?
因为在写框架时,无处得知以后要调用什么类,也就无法new对象,所以就必须用反射来解决这个问题