黑马程序员——学习日记之--反射的用法和Eclipse使用

来源:互联网 发布:手机宽带拨号软件 编辑:程序博客网 时间:2024/05/18 19:44

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

一:反射

  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中点双叉,去掉所有断点。

0 0
原创粉丝点击