JAVA反射

-什么是反射

-反射机制:反射就是Reflection,Java的反射是指程序在运行期可以拿到一个对象的所有信息;这种通过Class实例获取class信息的方法称为反射(Reflection)。

通过反射可以越过泛型检查。

-class类

除了int等基本类型外,Java的其他类型全部都是class(包括interface)。例如:

  • String
  • Object
  • Runnable
  • Exception

class是由JVM在执行过程中动态加载的。JVM在第一次读取到一种class类型时,将其加载进内存。

每加载一种class,JVM就为其创建一个Class类型的实例,并关联起来

-反射机制的功能/作用

  [在运行中]:

判断任意对象所属的类。

判断任意类所具有的成员变量和方法。

构造任意类的对象。

调用任意对象的方法。

生成动态代理(在运行时创建新类对象)。

-反射的优缺点

优点:

    反射提高了程序的灵活性、扩展性和低耦合。它允许程序创建和控制任何类的对象,无需提前硬编码目标类

缺点:

   (1)性能问题:反射基本上是解释操作,字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用。

   (2)模糊程序内内部逻辑:程序员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。比相应的直接代码更复杂。

 

-实现反射机制的类

   Java中主要由以下的类来实现Java反射机制(这些类都位于java.lang.reflect包中):

Class类:代表一个类。

Field类:代表类的成员变量(成员变量也称为类的属性)。

Method类:代表类的方法。

Constructor类:代表类的构造方法。

Array类:提供了动态创建数组,以及访问数组的元素的静态方法。

-如何使用反射

-获取class类对象的方式:

1、字节码未加载到内存

class.forName("全类名"):将字节码文件加载到内存,为程序返回class对象。

2、字节码文件以加入内存

类名.class:通过类名class属性获取

3、对象已存在

对象.getClass()

Class实例在JVM中是唯一的,所以,上述方法获取的Class实例是同一个实例。可以用==比较两个Class实例

 

Integer n = new Integer(123);

boolean b1 = n instanceof Integer; // true,因为n是Integer类型
boolean b2 = n instanceof Number; // true,因为n是Number类型的子类

boolean b3 = n.getClass() == Integer.class; // true,因为n.getClass()返回Integer.class
boolean b4 = n.getClass() == Number.class; // false,因为Integer.class!=Number.class

 

instanceof不但匹配指定类型,还匹配指定类型的子类。而用==判断class实例可以精确地判断数据类型,但不能作子类型比较。

 反射应用:

调用某个字段进行赋值、调用某个放法去做需求、

例: 

package reflectTest;

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

    public String a;
    protected String b;
    String c;

    //构造方法
    public Person() {
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    //成员方法
    public void eat(){
        System.out.println("吃.........");
    }
    public void eat(String xx,String zz){
        System.out.println("吃"+xx+"、"+zz);
    }


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

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

 

package reflectTest;

public class ReflectDemo01 {

    public static void main (String [] args) throws ClassNotFoundException {

        //1、获取全类名方式
        Class<?> clazz1 = Class.forName("reflectTest.Person");
        System.out.println(clazz1);//class reflectTest.Person

        //2、类名.Class
        Class<Person> clazz2 = Person.class;
        System.out.println(clazz2);//class reflectTest.Person

        //3、对象.getClass()
        Person ps =new Person();
        Class<? extends Person> clazz3 = ps.getClass();
        System.out.println(clazz3);//class reflectTest.Person

        //比较对象引用是否相同
        System.out.println(clazz1==clazz2);//true
        System.out.println(clazz1==clazz3);//true
    }
}

 从上面可以看出,他们三个字符串表现形式是一样的,对象引用是相同的。

-Class对象的功能

1、获取成员变量

1)、clz.getFields("变量名"):获得某个类的所有的公共(public)的字段,包括父类中的字段。

2)、clz.getDeclaredFields():获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段 .

Field []  getFields()、Field getField(String name)、getDeclaredFields()、getDeclaredField()

package reflectTest;

import java.lang.reflect.Field;

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

        Class<Person> personClass = Person.class;
        /*
         *Field操作:
         *  1.获取值
         *  2.设置值
         */
        System.out.println("==========getFields()==========");
        //getFields():获取所有public修饰的成员变量
        Field[] fields = personClass.getFields();
        for (Field field : fields) {
            System.out.println(field);//public java.lang.String reflectTest.Person.a
        }

        System.out.println("==========getField()==========");
        //getField(" "):获取指定内容的public修饰的成员变量
        //获取a的值:get(Object obj)
        Field a = personClass.getField("a");
        Person person = new Person();
        Object o = a.get(person);
        System.out.println(o);//null:未赋值默认空

        //设置a的值: void set(Object obj, Object value)
        a.set(person,"展示");
        System.out.println(person.a);//展示

        System.out.println("==========getDeclaredFields()==========");
        //getDeclaredFields()
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
            /*private java.lang.String reflectTest.Person.name
            private int reflectTest.Person.age
            public java.lang.String reflectTest.Person.a
            protected java.lang.String reflectTest.Person.b
            java.lang.String reflectTest.Person.c*/
        }
        System.out.println("==========getDeclaredField()==========");
        //getDeclaredField()
        Field b = personClass.getDeclaredField("b");
        //开启暴力反射:忽略访问私有时的非法访问异常
        b.setAccessible(true);
        System.out.println(b.get(person));//null

    }
}

2、获取成员方法

1) Clazz.getMethod(“方法名”,class……parameaType);(只能获取公共的)

2) Clazz.getDeclareMethod(“方法名”);(获取任意修饰的方法,不能执行私有)

让方法执行

1)Method.invoke(obj实例对象,obj可变参数);-----(是有返回值的)

getMethods()、getMethods()、getDecaredMethods()、getDeclaredMehods()

package reflectTest;


import java.lang.reflect.Method;

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

        Class<Person> personClass = Person.class;
        /*
        执行方法: Object invoke(Object obj, Object... args)
         */

        //获取指定名称的方法。获取方法三要素(方法名,参数列表,返回值)
        Method eat = personClass.getMethod("eat");
        Method eat1 = personClass.getDeclaredMethod("eat", String.class, String.class);

        Person person = new Person();
        eat.invoke(person);
        eat1.invoke(person,"xxx","zzz");


    }
}

 3、获取构造方法

1). Clazz.getConstructor([String.class]);

2). Con.newInstance([参数]);

其他方法:getConstructors()、getDecaredConstructors()、getDecaredConstructor()

package reflectTest;

import java.lang.reflect.Constructor;

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

        Class<Person> personClass = Person.class;
        /*
          构造方法
          作用:创建对象 T newInstance(Object... initargs)
         */
        System.out.println("==========getConstructor()===========");
        //获取构造方法
        Constructor<Person> constructor1 = personClass.getConstructor();
        System.out.println(constructor1);
        Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
        System.out.println(constructor);//public reflectTest.Person(java.lang.String,int)

        System.out.println("==========newInstance()===========");
        //创建对象
        Person person = constructor.newInstance("小红", 13);
        System.out.println(person);//Person{name='小红', age=13, a='null', b='null', c='null'}


    }
}

 --获取继承关系

.getSuperclass()

public class Main {
    public static void main(String[] args) throws Exception {
        Class i = Integer.class;
        Class n = i.getSuperclass();
        System.out.println(n);
        Class o = n.getSuperclass();
        System.out.println(o);
        System.out.println(o.getSuperclass());
    }
}
/*
class java.lang.Number
class java.lang.Object
null
*/

--动态代理

静态代码:

//定义接口:
public interface Hello {
    void morning(String name);
}
//编写实现类: public class HelloWorld implements Hello { public void morning(String name) { System.out.println("Good morning, " + name); } }
//创建实例,转型为接口并调用: Hello hello = new HelloWorld(); hello.morning("Bob");

动态代码,仍然先定义了接口Hello,但是并不去编写实现类,而是直接通过JDK提供的一个Proxy.newProxyInstance()创建了一个Hello接口对象。

在运行期动态创建一个interface实例的方法如下:

  1. 定义一个InvocationHandler实例,它负责实现接口的方法调用;
  2. 通过Proxy.newProxyInstance()创建interface实例,它需要3个参数:
    1. 使用的ClassLoader,通常就是接口类的ClassLoader
    2. 需要实现的接口数组,至少需要传入一个接口进去;
    3. 用来处理接口方法调用的InvocationHandler实例。
  3. 将返回的Object强制转型为接口。
public class Main {
    public static void main(String[] args) {
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method);
                if (method.getName().equals("morning")) {
                    System.out.println("Good morning, " + args[0]);
                }
                return null;
            }
        };
        Hello hello = (Hello) Proxy.newProxyInstance(
            Hello.class.getClassLoader(), // 传入ClassLoader
            new Class[] { Hello.class }, // 传入要实现的接口
            handler); // 传入处理调用方法的InvocationHandler
        hello.morning("Bob");
    }
}

interface Hello {
    void morning(String name);
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2020-03-24 21:34  鱼丸河粉  阅读(163)  评论(0编辑  收藏  举报