Bootstrap

java学习笔记第十一天

反射

Java中的反射(Reflection)是指在运行时检查或操作类、方法、字段等程序结构的能力。通过反射,我们可以在程序运行时动态地获取类的信息、调用方法、操作字段等,而不需要在编译时就确定这些操作。反射机制允许程序在运行时通过名称来获取类的信息,并且可以在运行时动态地创建对象、调用方法、访问属性等。

注意:反射会破坏类的封装性

反射的核心类是Class类

在Java中,反射机制主要由以下几个核心类和接口组成:

Class类:

代表类的字节码,每个类在JVM中都有对应的Class对象,可以通过该对象获取类的各种信息(如方法、字段、构造器等)。

Constructor类:

代表类的构造方法。

Field类:

代表类的成员变量(字段)。

Method类:

代表类的方法。

Array类:

提供了静态方法来动态创建和访问Java数组。

使用反射

关于类和对象的关系:
在Java中,类是对象的模板或蓝图。在创建对象之前,必须先定义和加载类。

static关键字的作用:
static关键字用于声明类级别的成员(属性和方法),它们属于类本身,而不是类的实例。

Class对象的作用:
每个类在运行时都有一个对应的Class对象,该对象包含了类的结构信息,如属性、方法、构造方法等。

Class对象和普通对象的区别:
Class对象是描述类的结构信息的特殊对象,而不是类的实例。它存储了类的静态信息,不涉及具体的对象实例化。

关于Class类:
Java中的Class类用于表示和操作类的结构信息,它是所有类的模板,但本身不能被实例化。

通俗的讲:
有对象必须先有类 static来修饰类的属性和方法
在java中存储了类的内容,这个内容也是一个对象
java中每一个类都有一个内存,这每一块内存都是一个对象
这些对象记录了这些类中声明了哪些属性和方法以及构造方法
java将这些类抽象为一个Class类
类的类对象中存储了类中定义的内容 属性/方法/构造方法
Class类的对象不能new

获取Class对象

在使用反射时,首先需要获取要操作的类的Class对象。有三种方式可以获取Class对象:

通过对象的getClass()方法
通过类名.class语法
通过Class.forName()方法
此三种方法在以下第二段代码中有演示。

开始演示前先定义要操作的属性:

public class Easy {
    public String name;
    protected String code;
    String sex;
    private String address;
    static int maxage;

    public static final transient String test=null;
    public Easy(){}
    public Easy(String name){
        this.name = name;
    }
    public void methodA(){
        System.out.println("methodA");
    }
    public void methodB(int a,int b){
        System.out.println("methodB");
        System.out.println("两个参数分别为"+a+","+b);
    }
    ...
    以下是name,code,sex,address,maxage五个属性的getter和setter方法,省略。

获取class对象的三种方法在此代码中有演示。

public class EasyClassA {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取类的类对象
        //通过类名获取类对象
        Class clazz = EasyClassA.class;
        //通过对象获取类对象
        clazz=new EasyClassA().getClass();
        //通过Class方法的forName方法获取
        clazz=Class.forName("com.easy725.EasyColor");
        System.out.println(clazz);
    }
}

类的类对象中存储了类中定义的内容 属性/方法/构造方法

Field类

java中用来记录类的属性的类叫做Field
getField和getFields 只能获取类中用public修饰的属性
反射访问私有属性 必须先获取访问权限
setAccessible(true)方法,默认为false

import java.lang.reflect.Field;
public class EasyClassB {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        //获取类对象
        Class c=Easy.class;
        c=new Easy().getClass();
        c=Class.forName("com.easy726.Easy");
        //获取类中的属性
        //fName变量指向的对象就是Easy类中name属性
        Field fName=c.getField("name");

        Easy easy=new Easy();
        easy.name="张三";
        System.out.println(easy.name);
        //可以获取某一个Easy类的对象的name属性的值
        Object objectName=fName.get(easy);
        System.out.println(objectName);
        //注入该属性的值
        fName.set(easy,"李四");
        System.out.println(easy.name);
        Field fCode=c.getDeclaredField("code");
        //设置code属性的值
        fCode.set(easy,"10001");
        Object obfCode=fCode.get(easy);//通过反射获取easy对象的code属性的值
        System.out.println(obfCode);

        Field fSex=c.getDeclaredField("sex");
        Field fAddress=c.getDeclaredField("address");
        fSex.set(easy,"女");
        //反射访问私有属性  必须先获取访问权限
        fAddress.setAccessible(true);
        fAddress.set(easy,"青岛");
        System.out.println(fAddress.get(easy));
    }
}

通过反射获取示例

public class easyClassC {

    public static <T> T getInstance(Class<T> tClass, Map values) throws InstantiationException, IllegalAccessException {
        //通过反射获取示例  创建对象
        T t=tClass.newInstance();//通过类中的无参构造方法创建对象
        //通过反射获取类中定义的属性
        Field[] farr=tClass.getDeclaredFields();
        //System.out.println(Arrays.toString(farr));
        for(Field fitem:farr){
            //获取属性的名字
            String fname=fitem.getName();
            //获取该属性在Map中的键值对   属性对应的值
            Object value=values.get(fname);
            //设置属性访问权限
            fitem.setAccessible(true);
            fitem.set(t, value);
        }
        return t;
    }
    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        Map map=new HashMap();
        map.put("code","C1001");
        map.put("name","张三");
        map.put("sex","女");
        Student stu=getInstance(Student.class,map);
        System.out.println(stu);
    }
}
class Student{
    private String code;
    private String name;
    private String sex;
...
以下代码为Student中三个属性的toString方法和getter、setter方法,省略

method类

主要功能和用途

获取方法信息:
Method 类允许你获取方法的名称、返回类型、参数类型等重要信息。通过反射,你可以在运行时检查和获取类的方法,即使在编译时你可能不知道具体的类和方法。

调用方法:method.invoke(对象)
通过 Method 对象,可以在运行时动态地调用方法。这对于需要根据条件决定调用哪个方法或者传递不同参数的情况非常有用。

public class EasyClassD {
    public static void main(String[] args) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        //获取类对象
        Class<Easy> c=Easy.class;
        //反射获取方法   Method
        Easy easy=c.newInstance();
        //获取public方法
        Method ma=c.getMethod("methodA");
        //调用方法  对象.方法名()   面向对象
        //method.invoke(对象)  反射的写法
        ma.invoke(easy);
        Method mb=c.getMethod("methodB",int.class,int.class);
        mb.invoke(easy,23,45);
    }
}

Constructor类

public class EasyClassE {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException, IntrospectionException {
        //反射获取构造方法
        Class<Easy> c=Easy.class;
        c.newInstance();//调用无参构造方法
        //获取无参构造方法
        Constructor<Easy> con=c.getConstructor();
        con.newInstance();//根据参数列表调用构造方法
        con=c.getConstructor(String.class);
        con.newInstance("张三");

        //修饰符  使用Modifier的方法判断方法/属性/构造方法的修饰符
        Field f=c.getDeclaredField("test");
        int fmod=f.getModifiers();
        System.out.println(fmod);
        boolean bool=Modifier.isStatic(fmod);
        System.out.println(bool);

        //内省   也是通过反射来实现的   内省不会破坏封装性
        //获取 BeanInfo
        BeanInfo bi=Introspector.getBeanInfo(c);
        //获取属性的写的方法和读的方法  setter/getter
        PropertyDescriptor[] pds=bi.getPropertyDescriptors();
        System.out.println(Arrays.toString(pds));
        String pName=pds[0].getName();//获取属性的名字
        System.out.println("属性名是"+pName);
        Method read=pds[0].getReadMethod();//对应属性的getter方法
        Method write=pds[0].getWriteMethod();//对应属性的setter方法
        //obj.setName("张三");//面向对象
        //write.invoke(obj,"张三");//反射
        Easy easy=c.newInstance();
        write.invoke(easy,"zhangsan");
    }
}

javaSE到此结束

;