反射的用法和Eclipse使用

一:反射

  1. 类加载器的概念

    类的加载分为三步:
    1 加载:通过类加载器将class文件读到内存,并创建Class对象
    2 连接:先验证类的内部结构是否正确,再给静态成员分配内存并默认初始化,然后解析类中的符号引用
    3 初始化:对非静态的成员初始化

    类加载器的组成
    Bootstrap ClassLoader :根类加载器,也叫引导类加载器,负责Java核心类的加载。
    Extension ClassLoader :扩展类加载器,负责Java的扩展目录中jar包的加载。
    System ClassLoader : 系统类加载器,负责在JVM启动时加载来自Java命令的class文件以及环境变量指定的jar包和类路径。

  2. Java反射机制:
    Java反射机制是在运行状态中,对于任意一个类都能否直到这个类的所有属性和方法。
    对于任意一个对象,都能否调用它的任意一个方法和属性,
    这种动态获取信息以及动态调用对象方法的功能称为Java的反射机制。

    反射简单讲就是,通过class文件对象去使用文件中的成员变量和方法。

    获取class文件对象的3种方式
     1 Object 类的getClass()方法。对于一个类的getClass()方法返回的Class对象只有一个。
     2  只要是数据类型,就可以拿到该类型的Class对象。
     3  Class类中的静态方法: forName()。注意要写类名的全路径。
    一般第二种比较简单方法,但开发时多使用第三种,可以获得一个字符串形式的类名,方便应用。

    对于一个Person类,可以通过3种方法获取class文件对象。

    Person p=new Person();
    Class c1=p.getClass();
    Class c2=Person.class;
    Class c2=Class.forName("package.Person");
    

    工具:先定义一个类Person供使用。

    class Person {
        private String name;
        private int age;
        private Person(){}
        public Person(String name,int age) {
            this.name=name;
            this.age=age;
        }
        private void show() {
            System.out.println("show run");
        }
        public String toString() {
            return name+" : "+age;
        }
    }
    

    举例一: 通过反射获取构造方法并使用。

    import java.lang.reflect.Constructor;
    class Demo {
        public static void main(String [] args) {
            //获取class文件对象
            Class c=Class.forName("Person");
    
            //获取构造方法
            Constructor[] cons=c.getConstructors();
            for (Constructor con : cons) {
                System.out.println(con);
            }
    
            Constructor con=c.getDeclaredConstructor();     //获取Person的单个构造方法
            Object oo=con.newInstance();        //获取Constructor对象的新实例
            System.out.println(oo);             //可以不导入Person类就打印Person对象
    
            Constructor con_2=c.getConstructor(String.class,int.class);     //获取指定参数的构造方法
            Object oo_2=con_2.newInstance("haha",12);   //创建新实例
            System.out.println(oo_2);               //打印Person对象
    
            Constructor con_3=c.getDeclaredConstructor(int.class);      //获取私有的构造方法
            con_3.setAccessible(true);                          //设置不进行访问检查,暴力访问。
            Object oo_3=con_3.newInstance(13);      //创建新实例
            System.out.println(oo_3);               //打印Person对象
        }
    }

    注意:
    getConstructors方法获取的是Person类中的公有构造函数
    getDeclaredConstructors方法获取Person类所有构造函数
    当要获取的构造方法不是共有的,就要用带Declared的方法获取,否则获取不到构造方法。
    暴力访问可以不让Java进行访问权限检查,不安全。

    举例二:通过反射获取成员变量并使用。

    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    class Demo {
        public static void main(String[] args) throws Exception {
            Class c=Class.forName("Person");
    
            //获取所有成员变量
            Field[] fields=c.getDeclaredFields();
            for (Field field : fields) {
                System.out.println(field);
            }
            //要先有对象
            Constructor con=c.getDeclaredConstructor(String.class,int.class);
            Object oo=con.newInstance("hh",1);
            System.out.println(oo);
    
            //获取单个成员变量
            Field field=c.getDeclaredField("age");      //获取成员变量age
            field.setAccessible(true);  //对私有的成员设置暴力访问
            field.set(oo,11);           //设置对象的age值为指定值。
            System.out.println(oo);     //打印修改后的值
        }
    }

    注意:
    获取成员变量要传入字符串名称。
    对成员变量的值使用时需要先创建对象。

    举例三:通过反射获取成员方法并使用。

    class Demo {
    public static void main(String[] args) throws Exception {
        Class c=Class.forName("Person");
    
        //获取本类所有成员方法
        Method[] methods=c.getDeclaredMethods();    
        for (Method method : methods) {
            System.out.println(method);
        }
    
        //创建对象
        Constructor con=c.getDeclaredConstructor(String.class,int.class);
        Object oo=con.newInstance("hh",1);
    
        //获取单个方法并使用
        Method method=c.getDeclaredMethod("show");      //获取单个私有方法。
        method.setAccessible(true);
        method.invoke(oo);      //对指定对象调用该方法。
    
        //获取带参数方法
        Method m=c.getMethod("toString");   //如果有参数,要加:参数.class
        Object o=m.invoke(oo);      //如果有返回值,要用Object接收
        System.out.println(o);
    }
    }

    注意:
    getMethods方法会获取本类和父类的所有公共方法。

  3. 示例:通过反射运行配置文件内容。
    配置文件properties.ini 含有键值对,className=methodName
    如果修改了配置文件的键与值,就可以改变程序的运行结果。
    所以不用修改源码,动态的加载类文件就可以完成程序修改。

    class Demo {
        public static void main(String [] args) throws Exception { 
            //读取配置文件
            Properties prop=new Properties();
            FileReader fr=new FileReader("properties.ini");
            prop.load(fr);
            fr.close();
    
            //获取键与值
            String key=prop.getProperty("className");
            String value=prop.getProperty("methodName");
    
            //通过反射获取
            Class c=Class.forName(key);
            Constructor con=c.getConstructor();
            Object oo=con.newInstance();
    
            Method m=c.getDeclaredMethod(value);
            m.invoke(oo);
        }
    }
  4. 通过反射越过泛型检查。
    对指定泛型的集合添加非指定类型的数据。
    泛型是给编译器看的,真正运行时,没有泛型概念。

    import java.util.*;
    class Demo
    {
        public static void main(String [] args) throws Exception
        {
            //新建ArrayList对象
            ArrayList<Integer> arrlist=new ArrayList<Integer>();
    
            //获取集合对象的class文件对象
            Class c=arrlist.getClass();
    
            //通过反射获取class文件对象的Object类型的add方法
            Method m=c.getDeclaredMethod("add",Object.class);
    
            //调用arrlist对象的add方法,传入字符串值。
            m.invoke(arrlist,"hello");
    
            System.out.println(arrlist);
        }
    }
  5. 动态代理
    在Java.lang.reflect包下提供了 Proxy 类和 InvocationHandler 接口,可以生成代理对象,
    JDK提供的代理只能针对接口做代理。用cglib做代理更强大。

    Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。

    Proxy 类中方法创建代理对象:

        public static Object newProxyInstance(ClassLoader loader, class<?>[] interface, InvocationHandler handler)
    

    InvocationHandler 中的唯一方法 :

        Object invoke(Object proxy, Method method, Object[] args)
    

    举例:

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.Method;
    
    interface User {
        void regist();
        void login();
    }
    
    class UserImpl implements User {
        public void regist() {
            System.out.println("注册");
        }
        public void login() {
            System.out.println("登录");
        }
    }
    
    class MyInvocationHandler implements InvocationHandler {
        //私有一个目标对象
        private Object target;
        public MyInvocationHandler(Object target) {
            this.target=target;
        }
    
        //实现接口中的唯一方法
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("权限校验");
            Object oo=method.invoke(target,args);
            System.out.println("日志记录");
            return oo;
        }
    }
    
    class Demo
    {
        public static void main(String [] args) throws Exception
        {
            User u=new UserImpl();
            u.regist();
            u.login();
            System.out.println("-----------------");
    
            //创建动态代理对象
            MyInvocationHandler myHandler =new MyInvocationHandler(u);
            User proxy=(User)Proxy.newProxyInstance(u.getClass().getClassLoader(), u.getClass().getInterfaces(), myHandler);
            proxy.regist();     //调用该方法会自动调用MyInvocationHandler接口中的invoke方法。
            proxy.login();
        }
    }
    注意:如果需要对其他类实现代理,只需要再创建其他类的代理对象。
    

二:Eclipse 常用快捷键

ctrl + D           :删除当前行
ctrl + shift + F   :常规格式化  
                    代码的布局格式:变量与符号的空格添加,括号的排位。
ctrl + shift + O   :导包  当多个包中有相同类时,需要手动选择哪一个
ctrl + / : 单行注释  取消单行注释再按一次快捷键
ctrl + shift + /   :多行注释  
ctrl + shift + \   :取消多行注释
alt + 上/下 :选中的代码上下移动
ctrl + alt + 上/下 :选中代码复制到上下行
ctrl + 点击        :查看源码
ctrl + O           :快速显示Outline大纲
ctrl + / (除号)    :折叠所有代码
ctrl + * (乘号)    :展开所有代码
alt + /            :内容辅助

自动生成构造方法:

alt + shift + S + c:无参构造函数
alt + shift + S + o:全参构造函数
alt + shift + S + r :选参构造函数

Debug 调试程序中断点的使用:
在程序中需要调试的有效语句的最左边双击,代表标记一个断点,可以在Debug视图中,单步走程序流程。
删除断点,在BreakPoint中点双叉,去掉所有断点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值