Java动态特性--反射


提示:本文属于基础语法和例子,个人复习整理,用于备忘,谢谢!!!!

动态特性简介:

Java中有一些动态特性,用来协助开发者在运行时编写出动态的代码,实现更优质的功能。比如反射、注解、动态代理、类加载器。利用这些特性,可以优雅地实现一些通过的功能,它们经常用于各种框架、库和系统程序中。比如:
(1) Springmvc、Jersey 用于处理web请求,利用反射和注解,能方便地将用户的请求参数和内容转换为Java对象,将Java对象转为响应内容。
(2) Spring、Guice 利用这些特性实现了对象管理容器,方便程序员管理对象的生命周期以及其中复杂的依赖关系。
(3) 应用服务器(如Tomcat)利用类加载器实现不同应用之间的隔离,JSP技术利用类加载器实现修改代码不用重启就能生效。
(4) 面向对象的编程(Aop)将编程中通用的关注点(如日志记录,安全检查等)与业务的主体逻辑相分离,减少冗余代码,提高程序的可维护性,Aop需要依赖上面的这些特性来实现。

反射:

它是运行的时候,动态获取类型的信心,比如接口信息、成员信息、方法信息,构造方法信息等。根据这些动态获取到的信息创建对象,访问/修改出成员,调用方法等。

(1) Class 类:

程序运行时,每个已经加载的类在内存都有一份类信息,每个对象都有指向它所属类信息的引用。
获取类对象的方法:

方法返回值说明
getClass()Class<?>Object 方法,通用获取Class对象方法
forNameClass<?>根据全限定类名直接加载Class

还有一种类名方式:直接类名.class,在写程序的时候就知道类名,就可以<类名>.class 。基础类型是没有getClass()方法的,只能直接类名的方式。示例:

Class<Integer> intCls = int.class;
Class<Double> doubCls = double.class;

(2) 名称信息:

Class有如下方法,可以获取与名称有关的信息:

方法返回值说明
getName()String获取java内部使用的名称
getSimpleNameString获取不带包名的名称
getCanonicalNameString获取更加友好的名称
getPackagePackage获取包信息

示例:

  Class<?> stringClass = String.class;
  //返回java内部使用的真正名称
  String name = stringClass.getName();
  System.out.println(name);
  //返回名称不带包信息
  String simpleName = stringClass.getSimpleName();
  System.out.println(simpleName);
  //返回的名称更加友好
  String canonicaName = stringClass.getCanonicalName();
  System.out.println(canonicaName);
  //返回包信息
  String packageName = stringClass.getPackage().toString();
  System.out.println(packageName);

(3) 字段信息:

类中定义的静态和实例变量都被称为字段,用Field表示,位于java.util.reflect下,下面是获取字段信息的方法:

方法返回值说明
getFields()Field []获取所有的public,包括父类的,如果没有字段,就返回空数组
getDeclaredFields()Field []获取本类所有字段,包括非public,但不包括父类的
getField(String name)Field获取本类或父类指定名称的字段,找不到抛出异常NoSuchFieldeException
getDeclaredField(String name)Field获取本类指定名称的字段,找不到抛出异常 NoSuchFieldeException

示例:

//直接类名.class,获取class对象
Class<?> refectClass = Refect.class;
//返回所有的public字段,包括其父类的,如果没有字段,就返回空数组
Field [] fields = refectFor.getFields();
//返回本类的申明的所有字段, 包括非public的,但不包括父类的
Field [] declaredFields = refectFor.getDeclaredFields();
//返回本类或者父类中指定名称的 public字段,找不到抛出异常NoSuchFieldException
Field field = refectFor.getField("name");
//返回本类中声明的指定名称的字段,找不到抛出异常NoSuchFieldException
Field declaredField = refectFor.getDeclaredField("schoolName");

Field 也有很多操作方法,用来获取字段的信息,也可以通过Field 访问指定对象中该字段的值,基本方法如下:

方法返回值说明
getNameString获取字段名称
isAccessible()boolean判断当前程序是否有该字段访问权限
setAccessible(boolean flag)void设置字段权限,true表示忽略java访问检查机制 ,运行读写非public的字段
get(Object obj)Object获取指定对象obj该字段的值
set(Object obj,Object value)void将指定对象obj中该字段的值设为value
getModifiers()int获取字段的修饰符
getType()Class<?>获取字段的类型
getAnnotation(Class annotationClass)T获取字段的注解信息

示例:

 //获取字段的名称
 String fieldName = field.getName();
 //class 创建对象的方法,默认调用无参构造,如果没有无参构造,则会抛出异常     instantiationException
 Refect refect = (Refect) refectFor.newInstance();
 refect.idCard = "12354616786";
 refect.schoolName = "中兴";
 //设置非public的读写权限
 declaredField.setAccessible(true);
 //获取指定对象该字段的值
 String schoolName = (String)declaredField.get(refect);
 //将指定对象的该字段值设为xxx
 declaredField.set(refect,"中国");
 System.out.println(schoolName);
 System.out.println(refect.getSchoolName());

(4) 方法信息:

类中定义的静态和实例方法都用类Method表示,类有如下方法:

方法返回值说明
getMehods()Method []获取所有pubic 方法,包括父类的,没有则返回空数组
getDeclaredMethods()Method []获取本类的所有方法,包括非public 的
getMethod(String name,Class<?>… paramterTypes)Method获取本类和父类中指定名称和参数类型的方法,找不到抛出异常NoSuchMethodException
getDeclaredMethod(String name,Class<?>… paramaterTypes)Method获取本类 指定名称和参数类型的方法,找不到抛出异常NoSuchMethodException

示例:

//返回所有的public方法,包括其父亲的,如果没有方法,返回空数组
Method [] methods = refectClass.getMethods();
//返回本类的所有方法,包括非public的,但不包括父类的
Method [] declaredMethods = refectClass.getDeclaredMethods();
//返回本类或者父类中指定名称和参数类型的public 方法,找不到抛出异常NoSuchMethodException
Method method = refectClass.getMethod("getAge",null);
//返回本类中声明的指定名称和参数类型的方法,找不到抛出异常NoSuchMethodException
Method declaredMethod = refectClass.getDeclaredMethod("setSchoolName", String.class);

Method类有许多操作方法用来获取方法的信息,基本方法有:

方法返回值说明
getNameString获取方法名称
setAccessible(boolean flag)void设置方法访问权限,true表示允许调用非public 方法
inoke(Object obj,Object – args,)Object在指定对象上调用Method代表的方法

示例:

 String methodName = method.getName();
 //设置非public方法是否允许调用
 method.setAccessible(true);
 //在指定对象obj上调用Method代表的方法,传递的参数列表为args,
 //对于invoke 方法,如果method为静态方,Obj被忽略,可以为null,args可以为 null,也可以为一个空的数组,方法调用
 //的返回值被包装为Object 返回,如果实际方法调用抛出异常,异常被包装为 InvacationTargetException重新抛出,
 //可以通过getCause()方法得到异常.
 declaredMethod.invoke(refect,"三小");

(4) 创建对象和构造方法

方法返回值说明
newInstance()T调用类无参构造创建对象,若没无参构造则抛出异常InstantiationException
getConstructors()Constructor<?> []获取所有public 构造方法,返回值可能时是长度为o空数组
getDeclaredConstructors()Constructor<?> []获取所有的构造方法,包括非public
getConstructor(Class<?>… paramterTypes)Constructor获取指定参数类型的public 构造方法,没有则抛出异常NoSuchMethodException
getDeclaredConstructor(Class<?>… paramterTypes)Constructor获取指定参数类型的构造方法,包括非public 的构造方法

(5) 类型检查和转换:

方法返回值说明
isInstance(Object obj)boolean用来判断动态类型
cast(Object)T用来转换动态类型

反射的简单应用示例:

package ref;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

/**
 * 反射应用实例,实现简单的序列/反序列
 * 实现简单的类,即有默认的构造方法,成员类型只有基础类型,包装类或者String
 */

public class ReflectCase {
    static class Stuent {
        private String name;
        private int age;
        private double secore;

        public Stuent() {
        }

        public Stuent(String name, int age, double secore) {
            this.name = name;
            this.age = age;
            this.secore = secore;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public double getSecore() {
            return secore;
        }

        public void setSecore(double secore) {
            this.secore = secore;
        }
    }

    /**
     * 序列化为字符串
     */
    public static String toString(Object obj){
        StringBuilder sb = new StringBuilder();
        try {
            Class<?> cls = obj.getClass();
            sb.append(cls.getName() + "\n");
            Field[] fileds = cls.getDeclaredFields();
            for (Field field : fileds) {
                if (!field.isAccessible()) {
                    field.setAccessible(true);
                }
                sb.append(field.getName() + "=" + field.get(obj).toString() + "\n");
            }
        }catch (IllegalAccessException e){
            e.printStackTrace();
        }
        return sb.toString();
    }

    /**
     * 反序列化为对象
     * @param str
     * @return
     */
    public static Object fromString(String str){
        String [] lines = str.split("\n");
        Object obj = null;
        if(lines.length < 1){
            throw  new IllegalArgumentException(str);
        }
        try {
            Class<?> cls = Class.forName(lines[0]);
             obj = cls.newInstance();
             if(lines.length>1){
                 for(int i = 1; i<lines.length; i++){
                     String [] fv = lines[i].split("=");
                     if(fv.length < 2){
                         throw new IllegalArgumentException("传入字符串格式不正确!");
                     }
                     Field field = cls.getDeclaredField(fv[0]);
                     if(!field.isAccessible()){
                         field.setAccessible(true);
                     }
                     setFieldValue(field,obj,fv[1]);
                 }
             }
        }catch (ClassNotFoundException e){
            e.printStackTrace();
        }catch (IllegalAccessException | InstantiationException e){
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }
    public static void setFieldValue(Field field,Object obj,String fieldValue) throws Exception{
           Class<?> type = field.getType();
           if(type == int.class){
               field.setInt(obj,Integer.parseInt(fieldValue));
           }else if(type == Byte.class){
               field.setByte(obj,Byte.parseByte(fieldValue));
           }else if(type == short.class){
               field.setShort(obj,Short.parseShort(fieldValue));
           }else if(type == long.class){
               field.setLong(obj,Long.parseLong(fieldValue));
           }else if(type == float.class){
               field.setFloat(obj,Float.parseFloat(fieldValue));
           }else if(type == double.class){
               field.setDouble(obj,Double.parseDouble(fieldValue));
           }else if(type == char.class){
               field.setChar(obj,fieldValue.charAt(0));
           }else if(type == boolean.class){
               field.setBoolean(obj,Boolean.parseBoolean(fieldValue));
           }else if(type == String.class){
               field.set(obj,fieldValue);
           }else{
               //当是简单对象时
               Constructor<?> constructor = type.getConstructor(new Class[]{String.class});
               field.set(obj,constructor.newInstance(fieldValue));
           }

    }

    public static void main(String[] args) {
        Stuent stuent = new Stuent("张三",19,80.0);
        System.out.println(toString(stuent));
        System.out.println(stuent);
        System.out.println(fromString(toString(stuent)));
    }
}

总结:

博主还只是一个小菜鸟,每天都在学习中,这是我自己看书整合出来的资料,每学习一个章节,都会写一篇博文来备忘。同时希望看我博文的小伙伴们能有好的收获,谢谢!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值