Java的高级特性反射

来源:互联网 发布:java里变量定义 编辑:程序博客网 时间:2024/06/05 00:51

---------------------------------------- android培训java培训、期待与您交流! ------------------------------------

首先简单说说什么反射,其实就是动态的加载类,我们在写JDBC的时候加载驱动Class.forName("xxxx"),这句话就涉及到了反射。

反射是自JAVA诞生就具备的高级特性,其强大的扩展能力使JAVA严谨死板的语法变得灵活。

但是能够超越一些JAVA对普通类的限定,有关反射主要相关的包在java.lang.reflect中。

但是反射也有缺点,就是结构代码较为复杂,使人难以理解,在编程中应当注视上普通的实现方式。

由于反射将一个类的各种成分都映射成了相应的类和对象,所以相对普通方法来说,比较消耗资源。

为什么要学习反射呢?

因为其强大的扩展性,在java开发中框架(Spring)大量应用了反射,反射是Java开发者非常有必要掌握的一门技能。

 

Java的反射机制主要提供了:

在运行时判断任意一个对象的所属的类。

在运行时判断构造任意一个类的对象。

在运行时判断任意一个类所具有的成员变量和方法。

在运行时调用任意一个对象的方法。

 

Class类(反射的基石)

Java中每个类都代表了一类事物,如Person类可以有张三李四的具体对象

Class是用于描述JAVA类的一个类,代表类的字节码实例对象

一个类被加载器加载到内存中,占用一片储存空间,在这个空间里的内容就是类的字节码

这样一个个空间可以用一类对象来表示,这些对象具备相同的类型,这个类型就是CLASS

//不同类的字节码是不同的,所以他们在内存中的内容也是不同的

 

java类的用于描述一类事物的共性,该类的属性是由该类的实例对象来决定的,不同的实例对象具备不同的属性

java程序中的各个JAVA类也属于同一类事物,而CLASS类就是用于描述这类事物的

 Class 类描述了类的属性信息,如类名、访问权限、包名、字段名称列表、方法名称列表等  

学习反射就要先搞清楚 Class 这个类  

获取各个字节码对应实例对象(class类型)的方法

1.类名.class ;如:Class c1= Person.class;

2.对象.getClass();如:new Date().getClass();

3.class.forName("类名");如:Class.forName("java.lang.String");

反射时主要用第三种,因为可以将表示“类名”的字符串定义一个变量

得到类的字节方式

                1.该类的字节码已经加载到了内存中,直接找到类的字节码返回(如上1,2)

                2.虚拟机中没有该字节码,用类加载器加载,将该字节码缓存起来同时返回(如3)

 

Class类的实例表示正在运行的JAVA应用程序中的类和接口。枚举是一种类,注释是一种接口。

每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。

基本的 Java 类型(booleanbytecharshortintlongfloat 和 double)和关键字 void 也表示为 Class 对象。

Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。    

  九个预定义Class实例对象 

   (八个基本数据类型+ void )每个类型都对应了一个 Class 实例对象   

 Class c1 = void.class;  

Class 类方法  

 isPrimitive();//判断该字节码是否是基本数据类型

 包装类不属于基本数据类型,但是可以通过 TYPE 常量获取其基本数据类型字节码;

 如:Integer.TYPE 与 int.class 是同一个字节码对象  

数组类型的Class实例对象判断方法: Class.isArray()

只要是在源程序中出现的类型,都有各自 Class 实例对象,如:int[],void 

----------------------------------------------------------------------  

"反射->Refelection"  

 反射并不是1.5版本才出现的,始于1.0,是java最重要的高级特性之一

概念:反射就是把Java类中的各种成分映射成相应的java类

 例如:Java中一个类可以用Class类的一个对象来表示

一个类的组成部分:成员变量、方法、构造方法、包等信息也用一个个的Java类来表示

//就像汽车是一个类,汽车中的发动机、变速箱等组成也是一个个类 

 表示Java类的Class类显然要提供一系列的方法,来获得其中的变量、方法、构造方法、修饰符、包等信息

这些信息就是用相应类的实例对象来表示,这些类是: Field,Method,Constructor,Package 等

 一个类中的每一个成员都可以用相应的反射API类的实例对象来表示,通过调用 Class 类的方法可以得到这些实例对象

 '得到这些实例对象后该怎么用,是学习应用反射的要点' 

 例如;  

 System.exit

System.getProperties() 

 Method -->   nethodObj1  可以使用两个 Method 对象来代表 System 类的两个方法

 methodOjb2  //Method代表方法这个类型,其对象代表具体是哪个方法 

----------------------------------------------- 

Constructor 类   代表某个类中的构造方法 

获取某个类所有的构造方法    // []数组,s获取多个 

 示例: Constructor[] constructor = Class.forName("java.lang.String").getConstructors();

  1,获取某一个构造方法     //可变参数 

 示例: Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class); 

//获得构造方法时要用到类型  

 2,创建实例对象 

 通常方式: String str = new String(new StringBuffer("abc")); 

 反射方式: String str = (String)constructor.newInstance(new StringBuffer("abc "));//实参 

 //调用方法时要用到上面相同类型的实例对象

Class.newInstance()方法:  

 ep: String obj = (String)Class.forName("java.lanng.String").newInstance();  //空参 

该方法首先得到默认的无参构造方法,然后使用该构造方法创建实例对象  

 该方法在内部使用了缓存机制来保存默认构造方法的实例对象,所以会影响性能

 只有两个类拥有newInstance()方法,分别是 Class 类和 Constructor 类

 Class类中的newInstance()方法是不带参数的,而Constructro类中的newInstance()方法是带参数的(Object),

 需要提供必要的参数。  在编程时要择需使用  

构造方法的反射: class-->constructor-->new object 通过两个步骤获取该类的对象 


-----------------------------------------------------------------------------------------------

Field 类  

   Field类代表某个类中的一个成员变量, 设有一个obj对象

   Field对象不是obj具体的变量值,而是指代的是obj所属类的哪一个变量,可以通过Field(对象).get(obj)获取相应的变量值

    示例: Field field = obj.getClass().getField("变量名")

   field.get(obj) //通过反射获取对象的变量值,参数是对象,意思就是获得那个对象的那个变量的值

   步骤: 1)获取class字节码  2)获取指定的Field对象  3)获取变量值  

 "暴力反射"  

    get方法只能获取声明为 public 的变量,对于私有变量,可以通过getDeclaredField()方法获取 private 变量

    获取对象后要通过 setAccessible(true)方法将该域设置为可访问

    示例: Field field = obj.getClass().getDeclaredField();  //1)2)获取私有file对象  

   field.setAccessible(true);  //3)将private变量设置为可访问;继承自父类 AccessibleObject 的方法

 field.get(obj);     //4)获取变量值 

反射替换 

 getField()方法获取一个.class 指定的public变量,getFields()方法获取该字节吗所有的 public 变量

 获取变量后就可以通过 field.set(obj,newValue) 将指定对象变量上此Field对象表示的值替换为新的值

 一个问题,我把自己的变量定义成private,就是不想让人家访问,可是,现在人家用暴力反射还是能够访问我,这说不通啊,

 能不能让人家用暴力反射也访问不了我。首先,private主要是给javac编译器看的,希望在写程序的时候,在源代码中不要访问我,

 是帮组程序员实现高内聚、低耦合的一种策略。你这个程序员不领情,非要去访问,那我拦不住你,由你去吧。

同样的道理,泛型集合在编译时可以帮助我们限定元素的内容,这是人家提供的好处,而你非不想要这个好处,怎么办?  

绕过编译器,就可以往集合中存入另外类型了。  

---------------------------------------------------------------------- 

Method 类  

 Method类代表某个类中的成员方法 

Method对象不是具体的方法,而是来代表类中哪一个方法,与对象无关 

示例:得到类中某一个方法:  

 Method methodCharAt = Class.forName("java.lang.String").getMethod("charAt",int.class) //方法名,int为参数

Class.getMethod方法用于得到一个方法对象,该方法接受的参数首先要有该方法名,

 然后通过参数列表来区分重载那个方法,参数类型用 Class 对象来表示(如为 int 就用 int.class)

调用方法:普通方式:str.charAt(1)  

反射方式:methodCharAt.invoke(str,1)  //参数1:调用哪个字符串。 参数2:实参int

//如果invoke方法接收的第一个方法是null,说明该方法是一个静态方法(不需要对象,如main方法)

jdk1.4 和jdk1.5 的invoke方法的区别: 

  1.5public Object invoke(Object obj, Object...args)  

1.4public Object invoke(Object obj, Object[] args)

//按照1.4的语法,需要将一个数组作为参数传递给invoke方法时,数组中每个元素分别对应被调用方法中的一个参数

//所以调用charAt方法可以用1.4的写法改为 methodCharAt.invoke("str", new Object[]{1}) 的形式  

用反射方式执行某个类中的"main"方法 

  学习目的:在不知道类名的情况下调用其main方法。  

 一般可以通过 类名.main(new String[]{"..."})来调用;如果不知道类名,而只是在程序中接收某一

 代表此main方法所属类的名称的参数,就需要用到反射

 要处理的问题:

  main方法的参数(String[] args)是一个字符串数组,通过反射调用该方法需要为invoke方法参数

 按照1.5的语法,整个数组是一个参数,而在1.4中数组的每个元素对应一个参数(会自动拆包)

1.5 为了兼容1.4,保留了该设定,所以在给main方法传递参数时,不能使用Method.invoke(null, String[]{...}) 

 因为编译器会将其按照1.4的语法进行编译,所以会出现"参数个数异常",这是1.4版本遗留的兼容性问题  

 处理方法: 

 Method.invoke(nullnew Object[]{new String[]{"..."}});//1,相当于加一层皮,拆分一次  

 Method.invoke(null, (Object)new String[]{"..."});//2,相当于声明为一个对象,不让编译器拆分

-----------------------------------------------------------------------------------------  

数组的反射

 具有相同维数和元素类型的数组属于同一个类型,即具有相同的 Class 实例对象 

 代表数组的Class实例对象的getSuperClass方法返回父类为Object类对应的Class

  基本类型的一维数组可以被当做Object类型使用不能当做Object[]类型使用

 非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用  //引用类型可以当Object[]类型使用

  String不是基本类型 

   Arrays.asList()方法处理 int[] 和 String[]时的差异:int[](Object); String[](Object 或 Object[]) 

   Array 工具类用于完成对数组的反射操作,专门用于处理数组的Object对象 

细节: Array 类与 Arrays 类的区分 

   java.util包中 Arrays 工具类对数组元素进行操作,接收参数为(Array[] array),无法处理数组的Object形态

    java.lang.reflect包中的 Array 工具类对数组对象进行操作,接收参数为(Object array)

  如何得到数组中的元素类型? 

   int[] a = new int[];    //即获取a前面数组的数据类型

    Object[] a = new Object{"String",1,true,'a'}  

     不能通过引用a获取来获取数组的数据类型,如Object示例

    只能获取该数组中某个具体的元素的数据类型;如: a[0].getClass().getName();

    【个人总结】 反射就是将java类中各种成员映射成java类,反射具有极其强大的扩展性,每个成员都是class,所以说JAVA中万物皆是对象

    可以通过一个对象获取该对象所属的字节码(运行时类),然后通过相应的方法获取其

    构造器、成员变量、成员方法等,然后将这些成分反作用给该对象,对该对象数据进行获取、修改等操作

    如:一个app没有源代码,只有打包的.class 字节码,如果想要对其进行修改升级等操作,无法通过修改源代码来实现

    这时就可以用到反射,通过改变运行时类来实现  


--------------------------------代码部分---------------------------------

package reflect;import java.lang.reflect.Array;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.Arrays;public class reflectTest {public static void main(String[] args) throws ClassNotFoundException,SecurityException, NoSuchMethodException, IllegalArgumentException,InstantiationException, IllegalAccessException,InvocationTargetException, NoSuchFieldException {String str = "abc";Class clazz = str.getClass();Class clazz2 = String.class;Class clazz3 = Class.forName("java.lang.String");System.out.println(clazz == clazz2);// trueSystem.out.println(clazz2 == clazz3);// trueSystem.out.println(clazz.isPrimitive());// 判断是否为基本类型System.out.println(int.class.isPrimitive());// trueSystem.out.println(int.class == Integer.class);// falseSystem.out.println(int.class == Integer.TYPE);// trueSystem.out.println(int[].class.isPrimitive());// falseSystem.out.println(int[].class.isArray());// 判断是否为数组// 构造方法的反射// 1.获取构造器Constructor<String> constructor1 = String.class.getConstructor(StringBuffer.class);// 2,使用Constructor的有参newInstance(obj) 方法创建对象,该参数是一个对象String str2 = (String) constructor1.newInstance(new StringBuffer("abc"));System.out.println(str2);// Field 类的反射// 1.获取public变量reflectBean re = new reflectBean("10", "11");Field field = re.getClass().getField("y");// getField获取指定public 变量System.out.println(field.get(re));// 获取re对象Y的值// 2, 获取私有变量 <-- 暴力反射 -->Field field2 = re.getClass().getDeclaredField("x");// 获取私有变量field2.setAccessible(true);// 设置访问权限System.out.println(field2.get(re));// 修改值changeStringValue(re);// Method 类的反射Method method = String.class.getMethod("charAt", int.class);// 反射方法System.out.println(method.invoke("str", 1));// str字符串对象调用此方法// 静态方法不需要对象,如果不是str而是null,说明该方法是静态方法System.out.println(method.invoke(str, new Object[] { 2 }));// jdk1.4的写法,一个元素代表一个参数// 用反射方式执行某个类中的main方法// (1)普通方式直接调用静态方法reflectBean.main(new String[] { "aa", "bb", "cc" });// (2)反射方式// String startingClassName = args[0]; //要在控制台传入参数,否则会抛出角标越界异常// Method mainMethod =// Class.forName(startingClassName).getMethod("main", String[].class);// jdk1.4遗留的兼容性问题:数组中每个元素分别对应被调用方法中的一个参数,所以会抛出参数个数异常// 解决方法:将该数组封装进一个Object对象// mainMethod.invoke(null, new Object[]{new String[]// {"aaa","bbb","ccc"}});//相当于包一层皮// mainMethod.invoke(null, (Object)new// String[]{"xxx","ooo","yyy"});//相当于声明不让编译器拆包// 数组的反射int[] a1 = new int[] { 1, 2, 3 };int[] a2 = new int[4];int[][] a3 = new int[3][4];String a4[] = new String[] { "a", "b", "c" };System.out.println(a1.getClass() == a2.getClass());System.out.println(a2.getClass().getName());System.out.println(a1.getClass().getSuperclass().getName());// 父类为ObjectObject obj1 = a1;// Object[] obj2 = a1; //基本数据类型的一维数组不能作为Object[]使用Object[] obj3 = a3; // 基本数据类型的多维数组可以作为Object或Object[]使用Object obj4 = a4;Object[] obj5 = a4; // 非基本元素类型一维数组可以作为Object或Object[]使用System.out.println(a1);System.out.println(a4);System.out.println(Arrays.asList(a1)); // a1是int基本类型,不能作为Object[]使用,只能作为一个ObjectSystem.out.println(Arrays.asList(a4)); // a4可以作为Object[]使用,符合jdk1.4的语法,自动拆分打印// Arrays工具类的静态方法:1.4版本: asList(Object[] a);将数组转成List集合,List集合可以直接打印// 1.5版本: asList(T...a) ; 向下兼容1.4 ,新增功能可以将多个参数转成List集合System.out.println(Arrays.asList(1, 2, 3, 4, 5));// asList方法1.5特性,可变参数printObject(a4);printObject("aaaaa"); }// 细节:Array类与Arrays类// java.util包中 Arrays工具类对数组元素进行操作,接收参数为(Array[] array),无法处理数组的Object形态// java.lang.reflect包中的Array工具类对数组对象进行操作,接收参数为(Object array)private static void printObject(Object obj) {Class clazz = obj.getClass();if (clazz.isArray()) {for (int i = 0; i < Array.getLength(obj); i++) {System.out.println(Array.get(obj, i));}}}private static void changeStringValue(Object obj)throws IllegalArgumentException, IllegalAccessException {Field[] fields = obj.getClass().getFields();for (Field field : fields) {if (field.getType() == String.class) {String oldValue = (String) field.get(obj);String newValue = oldValue.replace("11", "20");field.set(obj, newValue);}}}}




---------------------------------------- android培训java培训、期待与您交流! ------------------------------------


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 月子里喂奶腰疼怎么办 生完孩子子宫疼怎么办 记账凭证写错了怎么办 饥荒精神值为0怎么办 抓不住温暖我能怎么办 父亲打母亲我该怎么办 父亲对母亲家暴怎么办 摊上家暴的父亲怎么办 家暴警察不处理怎么办 父亲把母亲打了怎么办 u盘的文件打不开怎么办 头撞墙起包了怎么办 儿童头撞墙起包怎么办 头撞墙了鼓包了怎么办 北京65岁老年证怎么办 怎么办65岁免费乘车卡 5个月小孩晚上哭怎么办 婴儿要含奶头睡怎么办 我把输入法删了怎么办 头发干枯毛躁怎么办用什么洗发水 落枕后脖子歪了怎么办 睡觉睡得脖子疼怎么办? 婴儿脖子睡歪了怎么办 怀孕8个月肚皮痒怎么办 怀孕大阴唇肿了怎么办 孕妇肚皮撑的疼怎么办 我大阴唇特别长怎么办 切完洋葱辣眼睛怎么办 下昆山车没刷卡怎么办 高德地图用不了怎么办 玩游戏老是闪退怎么办 苹果平板电脑闪退怎么办 钉钉忘记打卡了怎么办 钉钉手机号换了怎么办 玖富叮当贷逾期怎么办 玖富超能怎么办现金贷 包包的油边开裂怎么办 lv包包油边开裂怎么办 lv钱包油边开裂怎么办 德运奶粉没勺子怎么办 音响坏了没声音怎么办