Java034——反射(Reflection)

一、Java中的反射及作用

Java的反射(reflection)机制是指在程序的运行状态中:

  1. 可以构造任意一个类的对象,
  2. 可以了解任意一个对象所属的类,
  3. 可以了解任意一个类的成员变量和方法,
  4. 可以调用任意一个对象的属性和方法。

一句话:这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。

二 认识.class类对象

这里回忆以下类的对象,然后举例说明.Class 类对象

2.1、类的对象

什么是类对象? 比如Dog类 Dog dog = new Dog();
dog 就是Dog类的对象。

2.2、Class 类型的对象

  1. 因为在Java中万物皆为对象,所以Dog 类也是一个对象,那他是谁的对象呢?
  2. 加载完类之后,在堆内存的方法区中就 产生了一个Class类型的对象(一个类只有一个Class对象),例如Dog类加载完成后,就会产生一个Dog 的Class类型对象
  3. 这个对象就像一面镜子,透过这个镜子看到类的结构,所以,才称这个为: 反射
  4. 这个对象包含了完整的类结构的信息( 类名 包名 实现接口 继承类 属性 方法 构造器 注解.... )

三、获取.class类对象

正常情况下,我们定义了一个类,要获得它的实例,是需要通过new关键字获取的,例如

class Dog{//定义了一个Dog
    //私有属性
    private String name = "Tom";
    //公有属性
    public int age = 18;
    
    //构造方法
    public Dog() {
    
    }
    //私有方法
    private void say(){
        System.out.println("private say()...");
    }
    //公有方法
    public void run(){
        System.out.println("狗急跳墙");
    }
}

class TestDemo{
    public static void main(String[] args) {
        Dog dog = new Dog();//通过关键字new获得Dog的一个对象dog

        dog.run();//dog对象调用run方法
    }
}

//运行结果
狗急跳墙

3.1、反射机制获取

一个类被加载后,类的整个结构都会被封装在Class对象中
Java034——反射(Reflection)_第1张图片
下面创建一个Dod类,类里面只有一个成员方法run()

package MyPackage;

class Dog{//一个类被加载后,类的整个结构都会被封装在Class对象中
    //公有方法
    public void run(){
        System.out.println("狗急跳墙");
    }
}

public class TestDemo{
    public static void main(String[] args) throws ClassNotFoundException {
//      1、方式一:通过对象获得
//      通过对象调用 getClass() 方法来获取,通常应用在:比如你传过来一个 Object
//      类型的对象,而我不知道你具体是什么类,用这种方法
        Dog dog = new Dog();
        Class c1 = dog.getClass();

        //2、方式二:通过类名 .class 获得
        // 该方法最为安全可靠,程序性能更高
        // 这说明任何一个类都有一个隐含的静态成员变量 class
        Class c2 = Dog.class;

        //3、 方式三:forName获得
        Class c3 = Class.forName("MyPackage.Dog");

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);

    }
}

//运行结果
class MyPackage.Dog
class MyPackage.Dog
class MyPackage.Dog

四、反射一般都用来做什么

  1. 可以构造任意一个类的对象,
  2. 可以了解任意一个对象所属的类,
  3. 可以了解任意一个类的成员变量和方法,
  4. 可以调用任意一个对象的属性和方法。

4.1、构造类的对象

package MyPackage;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class Dog {//一个类被加载后,类的整个结构都会被封装在Class对象中
    //私有属性
    private String name = "旺财";
    //公有属性
    public int age = 2;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    //无参构造方法
    public Dog() {

    }

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

    //公有方法
    public void run() {
        System.out.println(this.name+"狗急跳墙,"+this.age+"岁");
    }
}

public class TestDemo {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {

        Class c1 = Class.forName("MyPackage.Dog");
        //1.通过动态调用构造方法,构造对象
        System.out.println("-------------------");
        Dog dog1 = (Dog) c1.newInstance();
        dog1.run();

        //2.通过有参构造创建对象
        System.out.println("-------------------");
        Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class);
        Dog dog2 = (Dog)declaredConstructor.newInstance("小黑",4);
        dog2.run();

       // 3.调用普通方法
        System.out.println("-------------------");
        Dog dog3 = (Dog)c1.newInstance();
        Method setName = c1.getDeclaredMethod("setName", String.class);
        Method setAge = c1.getDeclaredMethod("setAge", int.class);
        setName.invoke(dog3,"小黄");
        setAge.invoke(dog3,1);
        dog3.run();

        // 4.获得指定成员变量
        System.out.println("--------------------");
        Dog dog4 = (Dog)c1.newInstance();
        Field name = c1.getDeclaredField("name");
        Field age = c1.getDeclaredField("age");
        name.setAccessible(true);

        name.set(dog4,"小白");
        age.set(dog4,3);

        dog4.run();
    }
}

//运行结果
-------------------
旺财狗急跳墙,2-------------------
小黑狗急跳墙,4-------------------
小黄狗急跳墙,1--------------------
小白狗急跳墙,3

4.2、获得类的信息

4.2.1、通过反射可访问的主要描述信息

Java034——反射(Reflection)_第2张图片

4.3、访问构造方法

4.3.1、Constructor 类的常用方法

Java034——反射(Reflection)_第3张图片
通过 java.langreflect.Modifier 类可以解析出 getModifiers0方法的返回值所表示的修饰符信息,在该类中提供了一系列用来解析的静态方法,既能查看是否被指定的修饰符修饰,又能以字符串的形式获得所有修饰符。

Modifier 类中的常用解析方法
Java034——反射(Reflection)_第4张图片
例如,判断对象 constructor 所代表的构造方法是否被 private 修饰,以及以字符串形式获得该构造方法的所有修饰符的典型代码如下:

int modifiers = constructor.getModifiers();
boolean isEmbellishByPrivate = Modifier.isPrivate(modifiers);
String embellishment = Modifier.toString(modifiers);

4.4、访问成员变量

4.4.1、Field 类的常用方法

Java034——反射(Reflection)_第5张图片

4.5、访问成员方法

4.5.1、Method 类的常用方法

Java034——反射(Reflection)_第6张图片

五、java类反射中所必须的类

java的类反射所需要的类并不多,它们分别是:field、constructor、method、class、object,下面将对这些类做一个简单的说明。

5.1、field类

提供有关类或接口的属性的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)属性或实例属性,简单的理解可以把它看成一个封装反射类的属性的类。

5.2、constructor类

提供关于类的单个构造方法的信息以及对它的访问权限。这个类和field类不同,field类封装了反射类的属性,而constructor类则封装了反射类的构造方法。

5.3、method类

提供关于类或接口上单独某个方法的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。这个类不难理解,它是用来封装反射类方法的一个类。

5.4、class类

类的实例表示正在运行的 java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 class 对象。

5.5、object类

每个类都使用 object 作为超类。所有对象(包括数组)都实现这个类的方法。

5.6、示例

  1. 获取类对象
    可以使用Class.forName()方法通过类的全限定名获取类对象。

    Class<?> cls = Class.forName("com.example.MyClass");
    
  2. 创建对象
    使用类对象的newInstance()方法可以创建类的实例。

    Object obj = cls.newInstance();
    
  3. 获取方法
    使用类对象的getMethod()方法可以获取指定名称和参数类型的公共方法。

    Method method = cls.getMethod("methodName", parameterTypes);
    
  4. 调用方法
    使用方法对象的invoke()方法可以调用方法。

    Object result = method.invoke(obj, args);
    
  5. 获取字段
    使用类对象的getField()方法可以获取指定名称的公共字段。

    Field field = cls.getField("fieldName");
    
  6. 设置字段的值
    使用字段对象的set()方法可以设置字段的值。

    field.set(obj, value);
    

以上仅是一些基本的反射操作示例,反射还有很多其他强大的功能,比如获取和操作私有方法/字段、获取注解信息、动态代理等。

需要注意的是,反射操作可能会带来性能上的开销,并且因为绕过了编译时的类型检查,容易导致类型错误,在使用反射时需要谨慎操作。

六、基本的反射操作示例

package MyPackage;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class Dog {//一个类被加载后,类的整个结构都会被封装在Class对象中
    //私有属性
    private String name = "旺财";
    //公有属性
    public int age = 2;

    protected String color="土黄色";

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    //无参构造方法
    public Dog() {

    }

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

    //私有方法
    private void say() {
        System.out.println("我叫"+this.name+"今年"+this.age+"岁");
    }


    //公有方法
    public void run() {
        System.out.println(this.name+"狗急跳墙,"+this.age+"岁");
    }
}

public class TestDemo {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {

        //获得名字
        System.out.println("--------------------获得名字");
        Class c1 = Class.forName("MyPackage.Dog");
        System.out.println(c1.getName());//获得类完整的名字,包括包名
        System.out.println(c1.getSimpleName());//只有方法名

        //获得类的public类型的属性(只能获取public修饰的)
        System.out.println("--------------------获得属性(只能获取public修饰的)");
        Field[] fields = c1.getFields();//获得类的public属性
        for (Field field : fields) {
            System.out.println(field);
        }

        //获得类的全部属性。包括私有的
        System.out.println("--------------------获得类的全部属性");
        Field[] declaredFields = c1.getDeclaredFields();//获得类的全部属性
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }

        //获得类的public类型的方法。这里包括 Object 类的一些方法
        System.out.println("--------------------获得方法");
        Method[] methods = c1.getMethods();//获得本类及其父类的所有public方法
        for (Method method : methods) {
            System.out.println(method);
        }

        //获得本类所有方法
        System.out.println("--------------------获得本类所有方法");
        Method[] declaredMethods = c1.getDeclaredMethods();//获得本类所有方法
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }
        //获得指定的属性
        System.out.println("--------------------获得指定的属性");
        Field age = c1.getField("age");
        System.out.println(age);

        //获得指定的私有属性
        System.out.println("--------------------获得指定的私有属性");
        Field name= c1.getDeclaredField("name");
		//启用和禁用访问安全检查的开关,值为 true,则表示反射的对象在使用时应该取消 java 语言的访问检查;反之不取消
        name.setAccessible(true);
        System.out.println(name);

        //获取所有构造方法
        System.out.println("--------------------获取所有构造方法");
        Constructor[] constructors = c1.getConstructors();
        for(Constructor constructor : constructors){
            System.out.println(constructor.toString());//public com.ys.reflex.Person()
        }
    }
}

//运行结果
--------------------获得名字
MyPackage.Dog
Dog
--------------------获得属性(只能获取public修饰的)
public int MyPackage.Dog.age
--------------------获得类的全部属性
private java.lang.String MyPackage.Dog.name
public int MyPackage.Dog.age
protected java.lang.String MyPackage.Dog.color
--------------------获得方法
public void MyPackage.Dog.setAge(int)
public void MyPackage.Dog.run()
public java.lang.String MyPackage.Dog.getName()
public void MyPackage.Dog.setName(java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
--------------------获得本类所有方法
public void MyPackage.Dog.setAge(int)
private void MyPackage.Dog.say()
public void MyPackage.Dog.run()
public java.lang.String MyPackage.Dog.getName()
public void MyPackage.Dog.setName(java.lang.String)
--------------------获得指定的属性
public int MyPackage.Dog.age
--------------------获得指定的私有属性
private java.lang.String MyPackage.Dog.name
--------------------获取所有构造方法
public MyPackage.Dog(java.lang.String,int)
public MyPackage.Dog()

Process finished with exit code 0

你可能感兴趣的:(Java全套学习笔记,java,ajax,javascript)