一、反射概述

反射:将类的各个组成部分(Class、Field、Constructor、Method)封装为其他对象(如Field对象、Constructor对象、Method对象),这就是反射机制

java代码在计算机中经历的三个阶段:源代码阶段→类对象阶段→运行时阶段

java文件通过编译(javac)生成字节码文件,java文件和字节码文件都在硬盘存储,此时为源代码阶段。

字节码文件通过类加载器(classLoader)加载进内存,内存中通过类对象来描述字节码文件,不管是什么字节码都有共同的属性,如成员变量、构造方法、成员方法。成员变量封装为Field对象,构造方法封装为constructor对象,成员方法封装为Method对象,这三个对象都在Class类中。一个类中可能有多个成员变量,故用Filed[]数组来描述所有的成员变量。构造器也可能有很多个, 故也用一个数组constructor[]来描述,同样,用Method[]数组来描述所有的方法。

好处:

1. 可以在程序运行过程中,操作这些对象。

  当我们定义了一个字符串,这个字符串会有很多方法提示,为什么呢?当字符串的字节码文件加载进内存,在内存中有一个class类对象,在类对象中已经将所有的方法抽取出来封装到Method[]数组中,进入展示出该字符串的所有方法,其实内部用到了反射机制。

2. 可以解耦,提高程序的可扩展性。

二、Class类对象

1、获取Class类对象的方式

1)、加载类对象

Class.forName("全类名"):将字节码文件加载进内存,返回Class类对象。

forName()是静态方法。

* 多用于配置文件,将类名定义在配置文件中。读取文件,加载类

Person实体类点击保存后就会自动编译,即有了字节码文件。

public class ReflectDemo1 {
    public static void main(String[] args) throws Exception {
        //1.Class.forName("全类名")
        Class cls1 = Class.forName("cn.itcast.domain.Person");
        System.out.println(cls1);
    }
}

结果:class cn.itcast.domain.Person

2)、获取类对象

类名.class:如果内存中已经有这个对象了,可以通过类名的属性class获取

* 多用于参数的传递

public class ReflectDemo1 {
    public static void main(String[] args) throws Exception {//2.类名.class
        Class cls2 = Person.class;
        System.out.println(cls2);
    }
}

结果:class cn.itcast.domain.Person

3)、如果已经有对象了,通过对象的getClass()方法获取

对象.getClass():getClass()方法在Object类中定义着。

* 多用于对象的获取字节码的方式。

public class ReflectDemo1 {
    public static void main(String[] args) throws Exception {//3.对象.getClass()
        Person p = new Person();
        Class cls3 = p.getClass();
        System.out.println(cls3);
    }
}

结果:class cn.itcast.domain.Person

那么这三种方式获取的类对象是一样的呢?

public class ReflectDemo1 {
    public static void main(String[] args) throws Exception {
        //1.Class.forName("全类名")
        Class cls1 = Class.forName("cn.itcast.domain.Person");
        System.out.println(cls1);
        //2.类名.class
        Class cls2 = Person.class;
        System.out.println(cls2);
        //3.对象.getClass()
        Person p = new Person();
        Class cls3 = p.getClass();
        System.out.println(cls3);

        //== 比较三个对象
        System.out.println(cls1 == cls2);//true
        System.out.println(cls1 == cls3);//true
    }
}

结果如下:

class cn.itcast.domain.Person
class cn.itcast.domain.Person
class cn.itcast.domain.Person
true
true

注意:==比较的是对象的内存地址,如果地址值相同,那么说明是同一个对象。

结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个

Person实体类

public class Person {
    private String name;
    private int age;

    public String a;
    protected String b;
    String c;
    private String d;


    public Person() {
    }

    public Person(String name, int age) {

        this.name = name;
        this.age = age;
    }

    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;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", a='" + a + '\'' +
                ", b='" + b + '\'' +
                ", c='" + c + '\'' +
                ", d='" + d + '\'' +
                '}';
    }


    public void eat(){
        System.out.println("eat...");
    }

    public void eat(String food){
        System.out.println("eat..."+food);
    }
}

2、Class对象的方法

获取功能:

1)、获取成员变量们Field[],目的是设置值和获取值

* Field[] getFields() :获取所有public修饰的成员变量
* Field getField(String name)   获取指定名称的 public修饰的成员变量
* Field[] getDeclaredFields()  获取所有的成员变量,不考虑修饰符
* Field getDeclaredField(String name)  

Field成员变量的方法:

1)、设置值

* void set(Object obj, Object value)  

2)、获取值

* get(Object obj) 

3)、忽略访问权限修饰符的安全检查

* setAccessible(true):暴力反射

例1:

public class ReflectDemo2 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;
        //1.Field[] getFields()获取所有public修饰的成员变量
        Field[] fields = personClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
    }
}

结果:

public java.lang.String cn.itcast.domain.Person.a

例2:

public class ReflectDemo2 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;
        Field a = personClass.getField("a");
        System.out.println(a);
    }
}

结果:public java.lang.String cn.itcast.domain.Person.a

获取成员变量的值:

public class ReflectDemo2 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;
        Field a = personClass.getField("a");
//        System.out.println(a);
        //获取成员变量a 的值
        Person p = new Person();
        Object value = a.get(p);
        System.out.println(value);
        //设置a的值
        a.set(p,"张三");
        System.out.println(p);
    }
}

结果如下:

null
Person{name='null', age=0, a='张三', b='null', c='null', d='null'}

例3:

public class ReflectDemo2 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;
        //Field[] getDeclaredFields():获取所有的成员变量,不考虑修饰符
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
    }
}

结果如下:

private java.lang.String cn.itcast.domain.Person.name
private int cn.itcast.domain.Person.age
public java.lang.String cn.itcast.domain.Person.a
protected java.lang.String cn.itcast.domain.Person.b
java.lang.String cn.itcast.domain.Person.c
private java.lang.String cn.itcast.domain.Person.d

例4:访问私有的成员变量

原来我们知道,私有的东西(如成员变量)不能在类的外边访问到,但是在暴力反射面其没有public和private,我的是我的,你的也是我的。

public class ReflectDemo2 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;        //获取成员变量a 的值
        Person p = new Person();//Field getDeclaredField(String name)
        Field d = personClass.getDeclaredField("d");
        //忽略访问权限修饰符的安全检查
        d.setAccessible(true);//暴力反射,设为true即忽略
        Object value2 = d.get(p);
        System.out.println(value2);
    }
}

注意:访问权限修饰符不是public修饰的成员的时候,在访问之前忽略权限修饰符的安全检查。

结果:null

getType() 和 getGenericType() 的区别

  • getType():返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型。
  • getGenericType():返回一个 Type 对象,它表示此 Field 对象所表示字段的声明类型。
public class ReflectDemo2 {
    public static void main(String[] args) throws Exception {
        Class personClass = Person.class;
        Field a = personClass.getField("a");
        Class<?> type = a.getType();
        Type genericType = a.getGenericType();
        System.out.println(type);
        System.out.println(genericType);
    }
}

结果如下:

class java.lang.String
class java.lang.String

二者输出的结果都是一样的,看似没有什么区别?接着看下面的例子:

先将a的类型改为T,如下所示:

public class Person<T> {
    private String name;
    private int age;

    T a;
    protected String b;
    String c;
    private String d;


    public Person() {
    }

    public Person(String name, int age) {

        this.name = name;
        this.age = age;
    }
//    private Person(String name) {
//        this.name = name;
//    }

    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;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", a='" + a + '\'' +
                ", b='" + b + '\'' +
                ", c='" + c + '\'' +
                ", d='" + d + '\'' +
                '}';
    }


    public void eat(){
        System.out.println("eat...");
    }

    private void eat(String food){
        System.out.println("eat..."+food);
    }
}

再次执行这段代码

public class ReflectDemo2 {
    public static void main(String[] args) throws Exception {
        Class personClass = Person.class;
        Field a = personClass.getField("a");
        Class<?> type = a.getType();
        Type genericType = a.getGenericType();
        System.out.println(type);
        System.out.println(genericType);
    }
}

结果如下:

class java.lang.Object
T

我们在源码中寻找下答案。

看下 getType() 的源码:

private Class<?> type;
public Class<?> getType() {
  return this.type;
}

getType 输出 this.type,而 type 是 Class 类型。

public Type getGenericType() {
        if (getGenericSignature() != null)
            return getGenericInfo().getGenericType();
        else
            return getType();
    }

如果 getGenericSignature 不为空,即如果当前类型是泛型,则返回泛型类型,反之返回 getType() 的值。

2)、获取构造方法们Constructor[],目的是用来创建对象,方法是newInstance()

* Constructor<?>[] getConstructors()  // 获取所有public修饰的构造器
* Constructor<T> getConstructor(类<?>... parameterTypes)  // 获取指定参数类型的pulic修饰的构造器
* Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)  
* Constructor<?>[] getDeclaredConstructors()

创建对象的方法:

* T newInstance(Object... initargs)  

* 如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法

例1:

public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;
        Constructor[] constructors = personClass.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
    }
}

结果如下:

public cn.itcast.domain.Person()
public cn.itcast.domain.Person(java.lang.String,int)

例2:

public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;
        //Constructor<T> getConstructor(类<?>... parameterTypes)
        Constructor constructor = personClass.getConstructor(String.class, int.class);
        System.out.println(constructor);
    }
}

结果:public cn.itcast.domain.Person(java.lang.String,int)

注意,每个构造方法传的参数不一样。

例3:

public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;//Constructor<T> getConstructor(类<?>... parameterTypes)
        Constructor constructor = personClass.getConstructor(String.class, int.class);
//        System.out.println(constructor);
        //创建对象
        Object person = constructor.newInstance("张三", 23);
        System.out.println(person);
    }
}

结果:Person{name='张三', age=23, a='null', b='null', c='null', d='null'}

例4:获取无参构造

public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;
        Constructor constructor1 = personClass.getConstructor();
        System.out.println(constructor1);
    }
}

结果:public cn.itcast.domain.Person()

例5:利用无参构造器创建对象

public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;
        Constructor constructor1 = personClass.getConstructor();        //创建对象
        Object person1 = constructor1.newInstance();
        System.out.println(person1);
    }
}

结果如下:

Person{name='null', age=0, a='null', b='null', c='null', d='null'}

例6:如果使用空参构造方法创建对象,操作可以简化:Class对象的newInstance方法。

public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;        Object o = personClass.newInstance();
        System.out.println(o);
    }
}

结果:Person{name='null', age=0, a='null', b='null', c='null', d='null'}

注意:以后使用空参构造器创建对象的化, 都使用Class对象的newInstance方法。

例7:使用私有的构造器创建对象

将Person类的构造方法修改如下:public改为private

private Person(String name, int age) {

        this.name = name;
        this.age = age;
    }

获取私有构造器

public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;//Constructor<T> getConstructor(类<?>... parameterTypes)
        Constructor constructor = personClass.getDeclaredConstructor(String.class, int.class);
        System.out.println(constructor);//constructor1.setAccessible(true);
    }
}

结果:private cn.itcast.domain.Person(java.lang.String,int)

使用私有的构造器创建对象

public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;
        Constructor[] constructors = personClass.getConstructors();
        Constructor constructor = personClass.getDeclaredConstructor(String.class, int.class);//创建对象
        Object person = constructor.newInstance("张三", 23);
        System.out.println(person);
    }
}

结果:报非法访问异常

Exception in thread "main" java.lang.IllegalAccessException: Class cn.itcast.reflect.ReflectDemo3 can not access a member of class cn.itcast.domain.Person with modifiers "private"
    at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
    at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
    at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:413)
    at cn.itcast.reflect.ReflectDemo3.main(ReflectDemo3.java:22)

使用暴力反射,即忽略安全检查

public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;
        Constructor[] constructors = personClass.getConstructors();
        Constructor constructor = personClass.getDeclaredConstructor(String.class, int.class);
        constructor.setAccessible(true);//创建对象
        Object person = constructor.newInstance("张三", 23);
        System.out.println(person);
    }
}

结果:Person{name='张三', age=23, a='null', b='null', c='null', d='null'}

3)、获取成员方法们Method[]

* Method[] getMethods()   // 获取所有public修饰的成员方法
* Method getMethod(String name, 类<?>... parameterTypes)   // 获取指定方法名称和参数类型的public修饰的成员方法
* Method[] getDeclaredMethods()  
* Method getDeclaredMethod(String name, 类<?>... parameterTypes)  

方法调用:

1、执行方法:

* Object invoke(Object obj, Object... args)  

2、获取方法名称:

* String getName:获取方法名

例1:获取所有public修饰的方法

public class ReflectDemo4 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;
//        //获取所有public修饰的方法
        Method[] methods = personClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
            String name = method.getName();
            System.out.println(name);
            //method.setAccessible(true);
        }
    }
}

结果如下:

public java.lang.String cn.itcast.domain.Person.toString()
toString
public java.lang.String cn.itcast.domain.Person.getName()
getName
public void cn.itcast.domain.Person.setName(java.lang.String)
setName
public void cn.itcast.domain.Person.setAge(int)
setAge
public void cn.itcast.domain.Person.eat()
eat
public void cn.itcast.domain.Person.eat(java.lang.String)
eat
public int cn.itcast.domain.Person.getAge()
getAge
public final void java.lang.Object.wait() throws java.lang.InterruptedException
wait
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
wait
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
wait
public boolean java.lang.Object.equals(java.lang.Object)
equals
public native int java.lang.Object.hashCode()
hashCode
public final native java.lang.Class java.lang.Object.getClass()
getClass
public final native void java.lang.Object.notify()
notify
public final native void java.lang.Object.notifyAll()
notifyAll

注意:Person类还有隐藏的继承Object的方法。

例2:获取指定名称的无参成员方法

public class ReflectDemo4 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;
        //获取指定名称的方法
        Method eat_method = personClass.getMethod("eat");
        System.out.println(eat_method);
    }
}

结果:public void cn.itcast.domain.Person.eat()

例3:调用无参成员方法

public class ReflectDemo4 {
    public static void main(String[] args) throws Exception {

        //0.获取Person的Class对象
        Class personClass = Person.class;
        //获取指定名称的方法
        Method eat_method = personClass.getMethod("eat");
        Person p = new Person();
       //执行方法
        eat_method.invoke(p);
    }
}

结果:eat...

例4:调用含参成员方法:

public class ReflectDemo4 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;
        Person p = new Person();        
Method eat_method2 = personClass.getMethod("eat", String.class); //执行方法 eat_method2.invoke(p,""); } }

结果如下:eat...饭

例5:执行私有的成员方法

修改Person类中的方法:

private void eat(String food){
        System.out.println("eat..."+food);
    }

调用私有方法

public class ReflectDemo4 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;
        Person p = new Person();        Method eat_method2 = personClass.getDeclaredMethod("eat", String.class);
//        //执行方法
        eat_method2.invoke(p,"");
    }
}

结果:报非法访问异常

Exception in thread "main" java.lang.IllegalAccessException: Class cn.itcast.reflect.ReflectDemo4 can not access a member of class cn.itcast.domain.Person with modifiers "private"
    at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
    at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
    at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
    at java.lang.reflect.Method.invoke(Method.java:491)
    at cn.itcast.reflect.ReflectDemo4.main(ReflectDemo4.java:23)

使用暴力反射

public class ReflectDemo4 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;
        Person p = new Person();        Method eat_method2 = personClass.getDeclaredMethod("eat", String.class);
        eat_method2.setAccessible(true);
        //执行方法
        eat_method2.invoke(p,"");
    }
}

结果:eat...饭

4)、获取全类名

* String getName()  

例1:

public class ReflectDemo4 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;
        //获取类名
        String className = personClass.getName();
        System.out.println(className);//cn.itcast.domain.Person
    }
}

结果:cn.itcast.domain.Person

posted on 2022-09-01 11:13  周文豪  阅读(44)  评论(0编辑  收藏  举报