zoukankan      html  css  js  c++  java
  • Java反射学习笔记


    1.什么是反射
    通常使用一个类需要先import"包.类" ----> 通过new实例化 ----> 取得实例化对象
    而反射:实例化对象 ----> getClass方法 ---->得到完整的“包.类”名称
    反射操作中一切的操作都是使用Object来完成的,类、数组的引用都可以使用Object进行接收。


    2.反射实现原理
    每一个类在编译时都会生成一个.class文件,JVM把每一个.class文件加载进内存后就会在内存中
    为每一个类创建一个class object,class object中包含包.类名称、构造方法、方法、属性。内存
    中有这个class object之后我们就可以使用它来实例化对象。注意在内存中一个class只有一个class object,
    这个class object是用来描述类本身的。

    3.反射对象实例化
    有三种方法来使用class object来实例化对象:
    (1) Class<?> c = Class.forName("包.类"); //c是一个泛型。
    (2) Class<?> c = new X().getClass(); //X是类名
    (3) Class<?> c = X.class

    然后使用c.newInstance();来实例化对象.
    错:
    Person p = c.newInstance(); //没有import Persion就不能直接使用Person
    对:
    Object p = c.newInstance(); //Object类是所有类的父类,newInstance获取一个Person类,再向上转化为Object类。

    4.对于一个类在内存中有一个class object, 对于基本数据类型,它也是一个类,它里面也有class,eg: int.class

    5.使用反射后就不需要import类了。但是没有import Person类,就不能直接使用Person类及其成员。

    6.若不想处理一个函数中的异常,又想编译时不报error,就可以加throws Exception把异常给扔出去。
    eg:
    public static void main(String args[]) throws Exception {
    }

    7.newInstance()实际上是去调用那个类的无参构造方法。若是没有写,类里面会有一个默认的什么都不做的无参构造方法(和C++一样)。
    若想使用有参构造方法来实例化对象,首先要获得它的构造方法,方法如下:
    Constructor<?> con = c.getConstructor(class...<?> parameterTypes); //注意参数类型也是一个class
    eg:
    Constructor<?> con = c.getConstructor(String.class); //获取构造方法,String为参数类型,要传入String.class
    Object P2 = con.newInstance("XiaoMing");

    使用Constructor需要import java.lang.reflect.Constructor;参考Android开发官网。

    8.获得并调用类的方法
    Method meth = c.getMethod(String name, Class...<?>parameterTypes); //参数:成员方法名,成员方法的参数类型
    Object obj = meth.invoke(Object receiver, Object...args);
    对于静态方法,invoke的第一个参数可以写为null.

    9.通过反射获取设置类/对象的属性
    有2种方法:
    (1)使用getMethod获得getter,setter方法,然后invoke这些方法来实现读取/设置属性。

    (2)使用Field
    ①获得属性
    Field f = c.getField(String name); //获得公共属性,此方法会先检索本类,再检索接口,最后检索其父类(?)。
    Field f = c.getDeclaredField(String name); //获得类中名为name的属性

    ②设置为可被外部访问(注意它会破坏类的封装性)
    f.setAccessible(true);

    ③调用get/set方法
    Object obj = f.get(Object objet); //object为实例化对象
    f.set(Object object, Object value);

    注意:getField只能获得公共属性,这些公共属性要么是本类的,要么是它实现的接口的
    要么是他的父类的。若想获得本类的所有属性可以使用getDeclaredField,它可以获取私有属性
    也可以是其它的public属性。

    10.使用反射的好处
    (1)增加程序的灵活性,比如可以通过参数传入类的名称,也可以通过配置文件传入类的名称,然后实例化出不同的对象。

    11.Me: 一个.java文件中只能定义一个public class, 但是可以定义多个非public的class
    error: class Reflect is public, should be declared in a file named Reflect.java

    12.Me: javac编译一个.java文件后,其内部的每一个类N都会生成一个N.class文件。

    二、例子

    demo1:Reflect.java实现三种方法实例化对象

    package a.b.c.d;
    
    class Person {
        private String name;
    
        void setName(String name) { this.name = name; }
        String getName() { return this.name; }
        
    };
    
    public class Reflect {
        public static void main(String args[]) {
            Person p = new Person();
            Class<?> c1 = null;
    
            try {
                c1 = Class.forName("a.b.c.d.Person");    //法一: 根据"包.类"来实例化对象
            } catch (ClassNotFoundException e) {
                System.out.println(e);
            }
            
            Class<?> c2 = p.getClass();        //法二
            Class<?> c3 = Person.class;     //法三
    
            System.out.println(c1.getName());
            System.out.println(c2.getName());
            System.out.println(c3.getName());
    
            int arr1[] = {1,2,3};
            int arr2[] = {1,2,3,4};
            int arr3[][] = {{1,2,3,4},{1}};
    
            Class<?> c4 = arr1.getClass();
            Class<?> c5 = arr2.getClass();
            Class<?> c6 = arr3.getClass();
    
            Class<?> c7 = int.class;            //基本类型也有一个class
    
            System.out.println(c4.getName());
            System.out.println(c5.getName());
            System.out.println(c6.getName());
            System.out.println(c7.getName());
    
            System.out.println((c4 == c5));    //都是一维数组,类别是一样的,这里是ture
            System.out.println((c4 == c6)); //false 一维数组和二维数组
        }
    }
    
    /*
    $ javac -d . Reflect.java 
    $ java a.b.c.d.Reflect
    a.b.c.d.Person
    a.b.c.d.Person
    a.b.c.d.Person
    [I
    [I
    [[I
    int
    true
    false
    */

    demo2: 使用成员方法和属性和反射增加灵活性

    Person.java

    package a.b.c.d;
    
    public class Person {
        public String name;
    
        public void setName(String name) { this.name = name; }
        public String getName() { return this.name; }
    
        public Person() {
            System.out.println("Constructor of Person");
        }
        public Person(String name) {
            this.name = name;
            System.out.println("Constructor2 of Person, name is "+this.name);
        }
    };

    Student.java

    package a.b.c.d;
    
    public class Student {
        public String name;
    
        public void setName(String name) { this.name = name; }
        public String getName() { return this.name; }
    
        public Student() {
            System.out.println("Constructor of Student");
        }
        public Student(String name) {
            this.name = name;
            System.out.println("Constructor2 of Student, name is "+this.name);
        }
    };

    Reflect.java

    //import a.b.c.d.Person;    //反射不需要import了
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    import java.lang.reflect.Field;
    
    public class Reflect {
        public static void main(String args[]) throws Exception {    //不想处理类中的异常,扔掉异常以免编译不过
            Class<?> c = null;
    
            try {
                c = Class.forName("a.b.c.d.Person");    //实例化对象,传入参数"a.b.c.d.Person"可以在运行时更改,改为args[1]获得灵活性
            } catch (ClassNotFoundException e) {
                System.out.println(e);
            }
    
            Object p = null;    //由于没有import Person不能直接使用Persion类,Object类是所有类的父类
    
            try {
                p = c.newInstance(); //这里默认使用的是无参的构造函数
            } catch (InstantiationException e) {
                System.out.println(e);
            }
    
            /*
             * 获取构造函数,想使用有参构造函数,参数为String,因此这里
             * 传参是String.class
             */
            Constructor<?> con = c.getConstructor(String.class);
            Object p2 = con.newInstance("xiaoming"); /*调用有参构造函数*/
    
            /*获取“setName()”成员方法*/
            Method set = c.getMethod("setName", String.class);
            /*分别是实例化对象p2和p调用setName()方法*/
            set.invoke(p2, "123");
            set.invoke(p, "abc");
    
            Method get = c.getMethod("getName");
            System.out.println(get.invoke(p));
            System.out.println(get.invoke(p2));
    
            /*获取属性成员name*/
            Field name = c.getDeclaredField("name");
    
            /*
             * 可以看出在使用setAccessible的时候破坏了类的封装性,name属性本来是私有的,
             * 现在强制被设置为可以被外界访问.
             * 所以一般不介意使用这个方法来操作成员变量,而是通过set/get方法访问。
             */
            //name.setAccessible(true); /*不设置它会产生异常,若是把name属性指定为public的,就不用设置了。*/
    
            /*设置属性成员name的值*/
            name.set(p, "www");    //设置p这个实例化对象的name属性,注意设置哪个对象的属性
            name.set(p2, "100ask");
            System.out.println(name.get(p));
            System.out.println(name.get(p2));
            
        }
    }

    使用:

    /*
    $ javac -d . Person.java 
    $ javac -d . Student.java 
    $ javac Reflect.java 
    $ java Reflect a.b.c.d.Person
    Constructor of Person
    Constructor2 of Person, name is xiaoming
    abc
    123
    www
    100ask
    $ 
    $ java Reflect a.b.c.d.Student
    Constructor of Student
    Constructor2 of Student, name is xiaoming
    abc
    123
    www
    100ask
    $
    */
  • 相关阅读:
    黑胶100
    界面滑动+ztree
    Linux下Java获取本机IP地址
    垂直对齐vertical-align:top
    针对jquery获取表单数据并且迭代方式
    js传递参数中文乱码
    前端开发资源库
    webpack加载器(Loaders)
    webpack基础知识点
    webpack操作基础
  • 原文地址:https://www.cnblogs.com/hellokitty2/p/10410119.html
Copyright © 2011-2022 走看看