继承(反射)

七、反射

反射机制是在运行态中,对于任何一个类/对象,通过反射都能知道任何一个属性和方法。这样动态获取新的以及动态调用对象方法的功能叫做反射。

1、Class类

Class可以说是反射能够实现的基础,

class关键字是在声明java类时使用的;而Class 是java JDK提供的一个类,完整路径为 java.lang.Class

对于每一种类,Java虚拟机都会初始化出一个Class类型的实例,每当我们编写并且编译一个新创建的类就会产生一个对应Class对象,并且这个Class对象会被保存在同名.class文件里。

当我们new一个新对象或者引用静态成员变量时,java(JVM)虚拟机中的类加载器系统会将对应的Class对象加载到虚拟机中,然后JVM再根据这个类型信息相关的Class对象创建我们需要实例对象或者提供静态变量的引用值。

note:每个class类不管创建多少实例对象,在JVM中都对用同一个Class对象。

java反射包java.lang.reflect中所有的类都没有public构造方法,要想获取这些类的实例,只能通过Class类来获取,如果想使用反射,就必须获得Class对象

 

1.1通过对象实例获取对应Class对象Object.getClass()----基本类型不能使用这样的方法。

//Returns the Class for String
Class c = "foo".getClass();

enum E { A, B }
//Returns the Class corresponding to the enumeration type E.
Class c = A.getClass();

byte[] bytes = new byte[1024];
//Returns the Class corresponding to an array with component type byte.
Class c = bytes.getClass();

Set<String> s = new HashSet<String>();
//Returns the Class corresponding to java.util.HashSet.
Class c = s.getClass();

 

1.2通过类的类型获取Class对象,基本类型也可以使用这种方法

//The `.class` syntax returns the Class corresponding to the type `boolean`.
Class c = boolean.class; 

//Returns the Class for String
Class c = String.class;

 

1.3通过类的全限定名获取Class对象,基本类型无法使用此方法

Class c = Class.forName("java.lang.String");//通过Class.forName()方法加载的类,采用的是系统类加载器
//对于数组比较特殊
Class cDoubleArray = Class.forName("[D");    //相当于double[].class

Class cStringArray = Class.forName("[[Ljava.lang.String;");   //相当于String[][].class

1.4 基本类型和void类型的包装类可以使用Type字段来获取

Class c = Double.TYPE;   //等价于 double.class.

Class c = Void.TYPE;

 

1.5通过Class获取类修饰符和类型

 

二、Member

反射定义了接口java.lang.reflect.Member,这个接口被java.lang.reflect.Field,java.lang.reflect.Method和java.lang.reflect.Constructor实现

类成员主要就包括构造函数、类变量和方法,而Member的这三个实现类就分别对应它们

java.lang.reflect.Field :对应类变量

java.lang.reflect.Method :对应类方法

java.lang.reflect.Constructor :对应类构造函数

反射就是通过这三个类才能在运行时改变对象状态。

 

1、突破java的权限检测

java运行时会进行访问权限检查,private类型的变量无法进行直接访问。

java.lang.reflect.AccessibleObject

AccessibleObject为我们提供了一个方法 setAccessible(boolean flag),该方法的作用就是可以取消 Java 语言访问权限检查。所以任何继承AccessibleObject的类的对象都可以使用该方法取消 Java 语言访问权限检查。

所以任何继承AccessibleObject的类的对象都可以使用该方法取消java语言的访问权限检查(final类型的变量也可以通过这种办法访问)

public final class Field extends AccessibleObject implements Member

FieldMethodConstructor都是继承AccessibleObject

 

2.Field

通过Field你可以访问给定对象的类变量,包括获取变量的类型、修饰符、注解、变量名、变量的值或者重新设置变量值,即使变量是private的。

 

2.1获取Field

Class提供了4种方法获得给定类的Field。

getDeclaredField(String name)

获取指定的变量(只要是声明的变量都能获得,包括private)

getField(String name)

获取指定的变量(只能获得public的)

getDeclaredFields()

获取所有声明的变量(包括private)

getFields()

获取所有的public变量

 

获取变量类型、修饰符、注解

public void testField(){
        Class c = Cat.class;
        Field[] fields = c.getDeclaredFields();

        for(Field f : fields){
            StringBuilder builder = new StringBuilder();
            //获取名称
            builder.append("filed name = ");
            builder.append(f.getName());
            //获取类型
            builder.append(" type = ");
            builder.append(f.getType());
            //获取修饰符
            builder.append(" modifiers = ");
            builder.append(Modifier.toString(f.getModifiers()));
            //获取注解
            Annotation[] ann = f.getAnnotations();
            if (ann.length != 0) {
                builder.append(" annotations = ");
                for (Annotation a : ann){
                    builder.append(a.toString());
                    builder.append(" ");
                }
            } else {
                builder.append("  -- No Annotations --");
            }
            Log.d(TAG, builder.toString());
        }
    }

获取、设置变量值

public void testField(){
        Cat cat = new Cat("Tom", 2);
        Class c = cat.getClass();

        try {
            //注意获取private变量时,需要用getDeclaredField
            Field fieldName = c.getDeclaredField("name");
            Field fieldAge = c.getField("age");
             fieldName.setAccessible(true);
            //反射获取名字, 年龄
            String name = (String) fieldName.get(cat);
            int age = fieldAge.getInt(cat);
            Log.d(TAG, "before set, Cat name = " + name + " age = " + age);
            //反射重新set名字和年龄
            fieldName.set(cat, "Timmy");
            fieldAge.setInt(cat, 3);

            Log.d(TAG, "after set, Cat " + cat.toString());
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

 

 

2.2 获取Method

①获取Method

Class依然提供了4种方法获取Method:

getDeclaredMethod(String name, Class<?>... parameterTypes)

根据方法名获得指定的方法, 参数name为方法名,参数parameterTypes为方法的参数类型,如 getDeclaredMethod(“eat”, String.class)

getMethod(String name, Class<?>... parameterTypes)

根据方法名获取指定的public方法,其它同上

getDeclaredMethods()

获取所有声明的方法

getMethods()

获取所有的public方法

Note:获取带参数的方法时,如果参数类型错误会报NoSuchMethodException,对于参数是泛型的情况,泛型须当成Object处理(Object.class)

 

②获取方法的返回类型

 

2.3 Constructor

获取构造方法

和Method一样,Class也为Constructor提供了4种方法获取

getDeclaredConstructor(Class<?>... parameterTypes)

获取指定构造函数,参数parameterTypes为构造方法的参数类型

getConstructor(Class<?>... parameterTypes)

获取指定public构造函数,参数parameterTypes为构造方法的参数类型

getDeclaredConstructors()

获取所有声明的构造方法

getConstructors()

获取所有的public构造方法

创建对象

通过反射有两种方法可以创建对象:

java.lang.reflect.Constructor.newInstance()

Class.newInstance()

一般来讲,我们优先使用第一种方法;那么这两种方法有何异同呢?

发布了71 篇原创文章 · 获赞 42 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/dreame_life/article/details/102608436