目录 :
1 ) . 透彻分析反射的基础_class类2 ) . 理解反射的概念3 ) . 构造方法的反射应用4 ) . 成员变量的反射
5 ) . 成员变量反射的综合案例6 ) . 成员方法的反射7 ) . 对接收数组参数的成员方法进行反射8 ) . 数组与Object的关系及其反射类型9 ). 数组的反射应用10 ). ArrayList_HashSet的比较及HashCode分析
一. 透彻分析反射的基础_Class类
1 ) . 反射的基石 -->Class类
1.1 简述 : java程序中的各个java类属于同一类事物,描述这类事物的java类名就是Class --> Person 类 与 Class类的理解
[1] 如同众多的人用一个类表示是用Person ,因此 众多的类用一个类表示就是Class[2] Person类实例化之后对应的是一个个的实体对象 ,那么Class实例化后对应的是一个个的字节码文件
1 .2 如何通过类对象得到对应的字节码文件?
[1] 方式一 : 类名.class 例如 : System.class[2] 方式二 : 对象.getClass() 例如 : new Data().getClass();[3] 方式三 : Class.forName("类名") 例如 : Class.forName("java.util.Data"); -->反射就是方式三,通过想Class类中传入变量的方式 获取字节码文件,然后实例化
1. 3 Class类中可通过方法获取 类的名字, 类的访问属性,类所属于的包名,字段名称的列表,方法名称的列表等2 ) . 小知识 :2.1 class 是用来定义对象的 类型 ,也理解为表示类的标识符-->开头字母小写c2.2 Class 是用来描述一类事物共性的类,也理解为实例类 --> 开头字母大写C
3 ) . Demo: Class对象的字节码文件的相关操作 ,获取字节码的三种方式 ,判断原始类型,数组的方式package cn.ittext.day01;public class ReflectTest{public static void main(String[] args) throws ClassNotFoundException{//-------获取String类对象字节码的三种方式String str ="ABC";//第一种 ,通过类自身获取sop(String.class);//第二种 ,通过内部方法获取sop(str.getClass());//第三种 ,通过外部Class类获取sop(Class.forName("java.lang.String"));//比较这三份字节码是否是同一份 -->true ,说明是同一份sop(String.class.equals(str.getClass()));sop(str.getClass().equals(Class.forName("java.lang.String")));//判断Int类型是否是原始类型int num=5;sop("int是否是原始类型:"+int.class.isPrimitive()); // truesop("int字节码与Integer字节码是否是同一份:"+int.class.equals(Integer.class)); //falsesop("int字节码在Integer中的字节码是否有同一份:"+int.class.equals(Integer.TYPE));// truesop("int[]数组字节码是否是原始类型:"+int[].class.isPrimitive());//false/** 小结:**[1] isPrimitive() ; 该方法用来判断是否是原始类型, 原始类型值8个基本数据类型**[2] Integer 包装类中 包含了 int 的字节码文件对象 ,而 Integer字节码是Integer的字节码 , Int类型的字节码 也是 Integer中的字节码** [3] 判断是否是数组 用方法 .inArray() ; 判断是否是 原始类型用 isPrimitive();** [4] int.class = Integer.TYPE ;**/}public static void sop(Object obj){System.out.println(obj);}}
小结 :1. 修饰符是用来描述访问权限(范围 :private protect default public ; 静态共享 : static ; 最终不改 : final), 类型是用来定义用途的, 返回值类型也就是返回的值的用途,2. 类如人的身体,类中的各种参数,方法,也理解为是人体的各个不同的部件 ,我们可通过 类获取类中的参数和方法3. java类用于描述一类事物的共性,该事物有什么属性,没什么属性,属性值是什么,则由这个类的实例对象决定,不同的实例化对象有不同的属性值4. 8个基本数据类型也有自己对应的class对象
二. 理解反射的概念
1 ) . 谨记 : 反射就是把java类中的各种成份映射成相应的java类2 ) . 理解 :
【1】 一个java类用一个Class 类的对象 来表示 , 一个类中的组成部分: 成员变量,方法,构造函数,包等信息也用一个个的java类来表示,如同汽车是一个类,汽车内部的发动机,变速箱等也都是一个个的类【2】 那么表示java类的class类显然要为不同的函数,变量提供一系列的类别名,例如 : fieild , method ,contructor ,package , 这些类别名 内 放着不同的对象实体
小结 :1. java中存在类别,我们大多情况下通过类别找到里边相应的内容2. 一个类中的每个成员都可用相应的反射API类的相应实例对象来表示,也就是说每个成员也是一个对象
三. 构造方法的反射应用 --> Construction类
1 ) . Demo:package cn.ittext.day01;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;public class ReflectApplication{public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException{//关于构造函数的运用//------------------------传统String tr =new String(new StringBuffer("123"));sop(tr.charAt(1));//-------------------------反射 完整版 : class字节码--> Construstor --> new Object//获取String的字节码文件,然后再获取String类中参数是StringBuffer 的构造方法, 这时 用 StringBuffer.class 字节码的方式表示参数,然后返回一个StringBuffered参数的构造函数Constructor constructor = String.class.getConstructor(StringBuffer.class); //传入参数类型//这时将对象传入这个方法newInstance()即可实现反射,前提是与上边获取的字节码构造函数一致String str = (String) constructor.newInstance(new StringBuffer("123")); //初始化传入的参数类型sop(str.charAt(1)); //获取角标为2的字符//-------------------------------反射 : 简化版 : calss字节码 --> new objectString str1 = (String) Class.forName("java.lang.String").newInstance();/**** 反射的两种对比 ,第一种可自己指定String传入的参数的构造函数, 而第二种直接使用的默认的空参构造函数** 因此:第一种用来在多参数构造对象时使用,第二种用在仅实例化对象时使用*/// --------------------------------------------------------------------------------------/*** 小结 :**方法总结:** Constructors[] constructors = String.class.getConstructors(); //获取String类字节码文件的所有构造函数** Constructor constructor String.class.getConstructor(StringBuffer.class); //获取String类字节码文件的单个构造函数 : StringBuffere类型的**思想总结:** 反射 代码看出: 宏观角度理解是就是 从 我需要,我创建 变成了 你提前做好,我随时会拿**问题总结:** 通过怎样的构造方法反射出来的对象就要传入对应的构造方法的参数值,否则报错**/}public static void sop(Object obj){System.out.println(obj);}}2 ) . 构造函数反射的步骤 :
2.1 第一步 : 获取想要对象的字节码文件2.2 第二步: 通过字节码文件获取相应参数构造函数2.3 第三步: 通过构造函数调用newInstance(相应参数)实例化对象并传入相应的参数
小结 :1. 我们可通过向构造函数中传入不同的参数个数和类型来选择自己需要的构造函数2. 反射会导致性能下降但效率提升
四. 成员变量的反射 -->Filed类
1 ) . Demopackage cn.ittext.day01;import java.lang.reflect.Field;public class ReflectApplciation01{public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{//关于成员变量的运用ReflectPoint rp = new ReflectPoint("张三", 18); //实例化对象//获取其对象中的私有成员变量Field fieldName =rp.getClass().getDeclaredField("name"); //通过对象变量获取到字节码文件,再通过字节码文件的获取公开变量的方法来获取私有变量的对象fieldName.setAccessible(true); //将获取到的类成员变量对象设置为可访问的sop(fieldName.get(rp)); //通过类成员变量对象获取其实例化对象相对应的值//获取其对象中的公开成员变量Field fieldAge = rp.getClass().getField("age"); //通过对象变量获取到字节码文件,再通过字节码文件的获取变量的方法来获取其类成员变量对象sop(fieldAge.get(rp)); //通过类成员变量对象获取其实例化对象相对应的值/*** 小结 :** 方法总结: 仅对以上代码的解析** [1] getClass() : 获取其对象的字节码文件对象** [2] getDeclaredField(); 获取其对象的私有成员变量对象** [3] getField(); 获取其对象的成员变量对象** [3] setAccessible(); 设置其对象的访问权限为可访问的** [4] get() ; 获取实例化对象中的对应参数值*** 经验总结:** [1] 当某个对象的成员私有,但我们想访问时,将此对象的成员变量的访问权限设置为可访问即可** [2] Field对象代表的是类中的一个成员变量对象,Field对象所关联的是类本身,而后通过类本身去取实例化后的对象的值 ,因为对象有多个,而类只有一个**/}public static void sop(Object obj){System.out.println(obj);}}2 ) . 知识点:
2.1 Field类代表某个类中的一个成员变量对象,通过它可获取其实例化对象中的变量值
小结 :1. 获取成员变量的方式 ,注意 私有和共有 , 思路是 获取其字节码文件对象,在获取其成员变量对象,通过成员变量对象获取实例化对象的相关参数值
五. 成员变量反射的综合案例
1 ) . pojo :package cn.ittext.day01;public class ReflectPoint02{public String start ;public String end ;public int age;public ReflectPoint02(String start, String end, int age) {super();this.start = start;this.end = end;this.age = age;}@Overridepublic String toString() {return "ReflectPoint02 [start=" + start + ", end=" + end + ", age=" + age + "]";}}2 ) . main :package cn.ittext.day01;import java.lang.reflect.Field;public class ReflectApplication02{//关于成员变量的案例/*** 需求 : 获取一个对象内的成员变量为String类型的,将他的值当中的T换成D** 步骤 :** [1]获取一个对象String类型的所有的成员变量对象** [2] 迭代出所有的成员变量对象** [3] 判断成员变量对象是否是String类型** [4] 获取到成员变量对象中的所有值** [5] 通过调用replace()方法来设置 需要替换的新值和老值** [6] 将替换后的数值通过set()放入成员变量中*** @throws IllegalAccessException* @throws IllegalArgumentException*/public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException{ReflectPoint02 RP =new ReflectPoint02("first","result",18);changeStringvAlue(RP);sop(RP);}private static void changeStringvAlue(Object obj) throws IllegalArgumentException, IllegalAccessException{Field[] fieldStrs = obj.getClass().getFields(); //通过获取对象的字节码文件而后获取到期对象的所有成员变量对象for(Field fieldStr :fieldStrs ) //迭代所有的成员变量对象{if(fieldStr.getType() == String.class) //判断成员变量类型是否为String类型{String oldValue = (String) fieldStr.get(obj); //通过成员变量对象获取其实例化对象相对应的值String newValue= oldValue.replace('t', 'd'); // 将所有值其中的t 改为d 并返回生成新的变量fieldStr.set(obj, newValue); //将更改的变量设置到实例化对象中}}}public static void sop(Object obj){System.out.println(obj);}}3 ) .小插曲 : 关于String中replace 替换的用法package cn.ittext.day01;public class Text {public static void main(String[] args){String str =new String("asdfgh");sop("start:::::::::"+str);String newValue = str.replace('a', 'q');sop("end:::::::::::"+newValue);}public static void sop(Object obj) {System.out.println(obj);}}
小结 :1. 两个字节码进行比较时用 ==比
六. 成员方法的反射 -->反射的核心原理总结 -->method方法
1 ) . Method : 代表某个类中的一个成员方法2 ) . Demo : 往常是通过类实例化对象而后调用方法,现在通过类直接获取方法而后作用于某个对象package cn.ittext.day01;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class ReflectApplication03 {public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{//关于成员方法的运用Method meTrim = String.class.getMethod("trim"); //通过String类直接获取到类中的方法String str =" abc ";sop("start:::::"+str);String str1 = (String) meTrim.invoke(str); //然后将方法给 str对象进行使用sop("end:::::"+str1);/**** 小结 :** 传统是我们通过 类 实例化对象 ,再通过对象 调用方法进行使用** 反射是我们通过类 获取其方法,再通过将方法给予需要的对象进行使用*** 理解 :** 类 -->对象 --> 方法 三者关系 :** [1] 类不是对象, 对象是类实例化后的结果,类是对象的抽象结果** [2] 类只有一个,而对象可通过类实例化多个,因此类与对象是一对多的关系** [3] 方法存在于类中,而对象只是调用者,而不是拥有者***///小插曲 ,关于 ==与equals的区别String strStart ="ASD"; String strEnd = new String("ASD");sop(strStart.equals(strEnd)); //比较内容sop(strStart==strEnd); //比较地址/**** 小结 :** String类型时 == 比较的是地址 , equals 比较的是内容 ; 则 其它类型== 与 equals 比较的都是地址***/}public static void sop(Object obj){System.out.println(obj);}}
小结 :1. Construction 代表构造函数的类别对象, Fileld代表成员变量的类别对象 , Method代表普通方法的类别对象2. 专家模式 : 谁拥有数据,谁就是操作这个数据的专家3. 面向对象 : 列车停车,是列车把车停了而不是司机把车停了,司机只是发了个信号罢了4. 在可变参数未出现之前,当需要传入多个参数的时候,我们使用的是通过数组进行接收
七. 对接收数组参数的成员方法进行反射
1 ) . 需求: 用反射的方式 去执行某各类中的主函数,也就是 main方法2 ) . Demo :package cn.ittext.day01;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class ReflectApplication04 {public static void main(String[] args) throws NoSuchMethodException, SecurityException, ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{//关于成员方法(main方法)的案例 ==>写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法//传统方式的调用使用// TestArguments.main(new String[] {"123","234"});//反射方式的调用使用String startingClassName =args[0]; //设置一个接收从外传入的对象存储变量Method mainMeth = Class.forName(startingClassName).getMethod("main", String[].class); //获取传入的对象的main方法对象,其参数类型是String[]mainMeth.invoke(null,(Object)new String[] {"123","234"}); //main方法对象调用自身方法并传入变量值new String[] {"123","234"}//一句话解释,就是 通过对象获取到其main方法,然后执行main方法时向里边传入了相应的值,而后运行/*** 问题解析 :** [1] 调用自身方法时 为何要用 (Object)进行转型** 因为 main方法会默认为 new String[]{"123","234"} 是俩对象会爆出 角标越界异常 ,因此 用 (Object)转型下成为一个对象** [2] 如何向main方法中传入对象值** 右击-->run as -->run configuration --> Agruments -->在里边写入对象地址,即传入了对象值 -->当每次运行时 都会携带我们设置好的对象值**/}public static void sop(Object obj){System.out.println(obj);}}class TestArguments{public static void main(String[] args){sop(args[0]+":::"+args[1]);}public static void sop(Object obj){System.out.println(obj);}}3 ) . 一句话解析 : 获取其传入对象的main方法,而后执行main方法并传入相应的值,而后运行即可 ;也就是 达到了 使用其他类中的main方法小结 :1. 如何向main方法中传入类上边有详细解析
2.为何要用反射? 运用反射就是 思维 从 我要 到 我造 的思维的 转换
八. 数组与Object的关系及其反射类型
1 ) . Demo :package cn.ittext.day01;import java.util.Arrays;public class ReflectApplciation05{public static void main(String[] args){/**** 本章讲述所用方法:** [1] Arryas.asList(); 将数组转化为List集合的方法** [2] get.Class(); 获取相关对象的字节码文件的方法*** //**** 庖丁解牛 : 数组** 1.类型** 2.维度** 3.长度** 结论 :** [1] 两个数组的类型相同,维度相同时,不管长度相不相同,字节码都是同一份** [2] 两个数组的类型与维度只要有一个不同,不管长度相不相同,字节码文件都不是同一份**/int[] in =new int[3];int[] in1 =new int[3];sop("类型相同,长度相同时:"+(in.getClass()==in1.getClass()));int[] in2 =new int[3];int[] in3 =new int[5];sop("类型相同,长度不同时:"+(in2.getClass()==in3.getClass()));int[][] in4 =new int[1][1];int[][] in5 =new int[1][1];sop("类型相同,维度相同时:"+(in4.getClass()==in5.getClass()));int[] in6 =new int[1];int[][] in7 =new int[1][1];// sop("类型相同,维度不同时:"+(in6.getClass()==in7.getClass())); 不可比较/**** 庖丁解牛 : 数组类型转为Object类型** 1.一维int类型转化** 2.二维int类型转化** 3.一维String类型转化** 4.二维String类型转化** 结论 :** [1] 一维的Int数组 是不能转化为 Object数组的,而二维的int数组是可以转化为Object数组的** [2] 无论是二维的什么类型的数组,当转化为Object[]数组时,通过asList()输出的都是 类型标识+数组标识+hash值** [3] 一维的String数组转化为Object[]数组时,通过asList()输出的是准确的内容值**/int[] ino1 = new int[] {1,2,3};Object o1 = ino1;int[][] ino2 = new int[][]{ {1,2,3},{2,3,4}};Object[] o2 = ino2;String[] sto1= new String[] {"A","B","C"};Object[] o3 = sto1;String[][] sto2= new String[][] { {"A","B","C"},{"D","F","G"}};Object[] o4 = sto2;sop("一维int类型转化"+Arrays.asList(o1));sop("二维int类型转化"+Arrays.asList(o2));sop("一维String类型转化"+Arrays.asList(o3));sop("二维String类型转化"+Arrays.asList(o4));/***** 庖丁解牛: getSupperClass()与class的关系讲述** 结论 :** [1] 无论是什么类型的父类字节码文件都是一个字节码文件java.lang.Object** [2] 也就是说 字节码也是一个体系 , 所有类型的字节码都有上地类***/int[] inc =new int[2];String[] stc =new String[2];String incl0 =inc.getClass().getSuperclass().getName();String stcl0 =stc.getClass().getSuperclass().getName();sop("int类型子字节码文件的父类字节码的名字"+incl0);sop("String类型子字节码文件的父类字节码的名字"+stcl0);sop(incl0==stcl0);}public static void sop(Object obj){System.out.println(obj);}}2 ) . 附加知识点 : -->主要结论在上边代码块中
2.1 当数组中标识有长度时,说明是空数组 ; 当数组中有值时,说明未标识准确长度
3 ) . 官方总结 :
3.1 具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象3.2 代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的class3.3 基本类型的一维数组可以被当做Object类型使用,不能当做Object[]类型使用; 非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用3.4 Arraays.asList()方法处理int[] 和String[]时的差异 :[1] 现象:当String类型的数组转化为list集合时,可以直接打印输出 值;而 int 类型的数组转化为list集合时,打印输出的是类型标识 数组标识 以及 值的hash值
[2] 原因 :(1)当是String类型数组调用asList()转化时,走的是jdk1.4版本的,那个版本传入多个参数时,接受方式是 数组类型 ,因此 是按多个值处理的,所以 可以直接打印输出(2) 当时int类型数组调用asList()转化时,走的是jdk1.5版本之后的,之后版本传入多个参数时,接受方式是可变参数类型, 因此是按一个值,处理的,所以打印输出的是 各种标识和hash值
小结 :1. 对象得到字节码需要使用方法getClass(), 类得到字节码仅需要.class即可
九. 数组的反射应用
1 ) . 官方概述 :
1.1 Array工具类用于完成对数组的反射操作1.2 思考题 : 怎么得到数组中的元素类型?
2 ) . Demo:package cn.ittext.day01;import java.lang.reflect.Array;public class ReflectApplication05{public static void main(String[] args){String[] str=new String[] {"A","B","C"};/*** 庖丁解牛 : 输出数组内的元素** 1.传统方式** [1] getClass(); 获取其字节码文件** [2] isArray() ; 判断是否是数组** [3]for() ; 加强for循环搞定迭代** 2.反射方式** [1] Array ; 此类中是通过反射的方式 获取 其他数组的 一些基本属性***/printObject(str);printObject1(str);/**** 庖丁解牛: 通过反射获取一个类型的类型***/String[] str1 =new String[] {"A","B","C"};String strName = str1.getClass().getTypeName();sop("str类型"+strName);}//传统方式private static void printObject(Object[] obj){if(obj.getClass().isArray()){for (Object object : obj){System.out.println(object);}}}//反射方式private static void printObject1(Object[] obj){if(obj.getClass().isArray()){int len = Array.getLength(obj); //通过反射的方式获取到长度for(int i=0;i<obj.length;i++){System.out.println(obj[i]);}}}public static void sop(Object obj){System.out.println(obj);}}
小结 :1. 请谨记 当需要获取 数组的相关属性是 ,可通过 数组的反射类Array进行获取
十. .ArrayList_HashSet的比较及HashCode分析
1 ) . Demo:package cn.ittext.day01;import java.util.ArrayList;import java.util.Collection;import java.util.HashSet;public class ReflectApplication06{public static void main(String[] args){/*** 庖丁解牛 : 分析相同数据在 ArrayList和hashSet的存储占位*** 结论 :** [1] 在ArrayList中重复元素都是会新开辟空间存储的** [2] 在hashSet中重复元素都是不会新开辟空间存储的,而是在原元素上建立引用,前提是 其pojo 覆盖了 equals和HashCode方法**/ReflectPoint06 rl0 =new ReflectPoint06("马六",2);ReflectPoint06 rl1 =new ReflectPoint06("马六",2);Collection<ReflectPoint06> arr =new ArrayList<ReflectPoint06>();arr.add(rl0);arr.add(rl1);sop(arr.size());Collection<ReflectPoint06> hs =new HashSet<ReflectPoint06>();hs.add(rl0);hs.add(rl1);sop(hs.size());/***** 庖丁解牛 : 聊聊内存泄露的一种情况** 背景 : 在hashSet的背景下,对hashSet中的pojo值进行改动,然后进行删除** 现象 : 我们发现根本删除不了** 原因 : 因为HashSet在给每一个pojo分配hashCode然后进入不同的区域后 ,我们又对 pojo做了改动,这个时候,不能明确是在哪个区域,因此删除失败** 总结 : 尽量不要在程序运行过程当中对 hashset中的pojo进行改动,改动会触动 hashCode,出现内存溢出的情况***/rl0.setAge(88);sop("删除之前的长度"+arr.size());hs.remove(rl0);sop("删除之后的长度"+arr.size());}public static void sop(Object obj){System.out.println(obj);}}2 ) . 结论 :
2.1 向ArrayList集合中添加元素,无论值重复与否,按正常顺序依次插入,从位置的角度讲是有序的2.2 向HashSet集合中添加元素,当值重复时,则不添加,直接使用其引用标识即可,当值不重复时,再添加
3 ) . HashSet底层比较hashCode 值的方式是什么?
3.1 以某种标识划分几个大的区域,每个类型的hashcode 值 根据判定放入不同的区域内3.2 比较时,先比较此类型的hashcode 值是属于哪个区域,然后在指定区域进行查找判定,3.3 当出现重复元素时,即不将元素加入集合,即可实现了hashset值的不可重复
小结 :1. 所谓的面向接口编程就是面向父类编程,就是 将所用实现类前的返回类型值写成该实现类的父类 或 顶级接口2. 每一个class对象的hashcode值是固定的,hashCode的应用价值在于使用了哈希存储的结合hashSet3. 当某一程序已不具备实用价值,长期搁置, 但内存一直未被释放,占用内存空间,最终导致内存溢出4. 内存泄露是指某些应该释放空间的程序出现了BUG导致内存不释放,不断占用内存空间,内存溢出是指内存不够用了,内存泄露是内存溢出的其中一种原因
十一. 总结
1 ) . 什么是class? java程序中的各个java类属于同一类事物,描述这类事物的java类名就是Class , 可理解为一种 类别2 ) . 什么是反射? 反射就是把java类中的各种成份映射成相应的java类3 ) . 反射的应用层面有哪些? construction构造函数的反射 , Filed 成员变量的反射 , Method 成员函数的反射 ,数组的反射,4 ) . 数组的反射类 array的应用 -->通过此类可对数组的基本元素及相关属性进行反向获取5 ). ArrayList , HashSet 及HashCode分析:
5.1 ArrayList 是有序的,可重复的,有坐标的5.2 HashSet是无序的,不可重复的,无坐标的, 底层存储依靠的是 hashCode值 分不同区域存储