Java反射机制

Java 反射是一种强大的特性,允许程序在运行时检查和操作类、方法、字段等。通过反射,Java 程序可以动态地加载类、创建对象、调用方法和访问字段,而无需在编译时知道这些类的具体信息。

什么是反射?

反射(Reflection)是一种Java程序运行期间的动态技术,可以在运行时(runtime)检查、
修改其自身结构或行为。通过反射,程序可以访问、检测和修改它自己的类、对象、方法、属性等成员。


反射的主要用途

  • 动态加载类:程序可以在运行时动态地助加载类库中的类;
  • 动态创建对象:反射可以基于类的信息,程序可以在运行时,动态创建对象实例;
  • 调用方法:反射可以根据方法名称,程序可以在运行时,动态地调用对象的方法(即使方法在编写程序时还没有定义):
  • 访问成员变量:反射可以根据成员变量名称,程序可以在运行时,访问和修改成员变量(反射可以访问私有成员变量):
  • 运行时类型信息:反射允许程序在运行时,查询对象的类型信息,这对于编写通用的代码和库非常有用
  •         Spring框架使用反射来自动装配组件,实现依赖注入;
  •         MyBatis框架使用反射来创建resultType对象,封装数据查询结果:

反射的优点

增加程序的灵活性
        反射允许程序在运行时动态地访问和操作类的属性和方法,这意味着开发者可以根据需要,在程序执行过程中改变程序的行为,而无需在编译时硬编码这些行为。这种灵活性使得程序能够适应不同的运行环境或需求变化。
避免将固有的逻辑程序写死到代码里
        通过反射,开发者可以将一些决策逻辑或行为延迟到运行时进行,而不是在编译时就确定下来。这有助于减少代码中的硬编码,提高代码的可维护性和可扩展性。
提高代码的复用率
        反射使得开发者可以编写一些通用的函数或方法,这些函数或方法能够动态地敞处理不同类型的对象或方法。这有助于提高代码的复用率,减少重复代码的编写。
支持动态代理和动态配置
        通过反射,开发者可以在运行时动态地创建代理对象,从而实现对目标对象的代理。这种机制在AOP(面向切面编程)等领域中非常有用。此外,反射还支持在运行时读取和修改程序的配置参数,从而实现动态配置。
支持自动化测试和代码生成
        反射技术可以应用于自动化测试中,通过动态地创建测试用例来验证程序的行为。同时,反射还可以用于在运行时生成程序代码,这在某些需要动态构建程序结构的场景中非常有用。

自由度高,可以无视访问权限限制
在某些情况下,反射可以绕过JaVa等语言的访问控制机制,直接访问私有成员。虽然这可能会带
来一些安全风险,但在某些特定的应用场景中,这种能力是非常有用的:

反射的缺点

  • 性能开销:反射操作通常比直接代码调用更慢,因为涉及到额外的检查和解析步骤。
  • 安全问题:如果允许程序修改私有成员或调用私有方法,可能会破坏封装性和安全性。
  • 代码可读性和可维护性:过度使用反射可能会使代码难以理解和维护,因为它隐藏了类型之间的依赖关系。

Java如何实现反射?

  • 在 JDK中,主要由以下类来实现 Java 反射机制:
  • Class类:表示正在运行的Java应用程序中的class类型信息的实例;
  • Field 类:提供有关类的属性信息,以及对它的动态访问权限。它是一个封装反射类的属性的类;
  • Constructor类:提供有关类的构造方法的信息,以及对它的动态访问权限。它是一个封装反射类的构造方法的类;
  • Method 类:提供关于类的方法的信息,包括抽象方法。它是用来封装反射类方法的一个类;

获取Class对象的三种方式

  • 第一种方式:Class.forName(类名)是通过类的完全限定名获取 Class 对象,这也是平时最常用
  • 的反射获取C1ass 对象的方法:
  • 第二种方式:类名.class,通过类名的class属性获取Class对象;
  • 第三种方式:对象·getClass(),通过对象的getclass()方法获取class 对象,该方法由Object 提供。

反射的使用场景 

  • 使用JDBC连接数据库时,使用Class.forName(驱动类名),通过反射,动态动加载当前程序中的数据库驱动类;
  • Spring框架的IOC(动态动加载管理Bean创建对象以及AOP(动态代理)使用反射进行底层实现:
  • MyBatis框架可以通过反射实现Mapper接口代理对象的创建。以及解析SQL【映射文件中resultType指定的类型信息后,通过反射动态创建对象,进行查询结果集的封装:

反射相关的类:

  • Class类

        封装类型信息

        获取“构造器”,“成员变量”,“方法”

  • Constructor类:

        每个Constructor对象,都代表一个构造器(构造方法)

        作用:创建目标对象

  • Field类:

        每个Field对象,都代表一个成员变量

        作用:将值(数据)存入目标对象中的指定成员变量中

  • Method类:

        每个Method对象,都代表一个方法

        作用:执行方法逻辑 

Java 反射的主要功能

 一、对象的创建

        可以使用 Class.forName() 方法加载类,并通过 newInstance() 方法创建对象

public class Test01 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {

        //编译器
        //硬编码的方式创建
        Order o1=new Order();

        //运行期
        //“反射”的方式创建对象
        String className="com.xn.test.Test01";
        Class classinfo=Class.forName(className);
        Object o2=classinfo.newInstance();

        System.out.println(o1);
        System.out.println(o2);

    }


}
class Order{

}

 二、获取类的信息

        使用 Class 类可以获取类的名称、父类、接口、构造方法、方法和字段等信息                

1、获取 Java 类对象(Class 对象)的方法

public class Test02 {
    public static void main(String[] args) throws ClassNotFoundException {
        //方式1:通过类名
        Class stringClass1=String.class;

        //方式2:通过Class类的forName()方法
        Class stringClass2=Class.forName("java.lang.String");

        //方式3:通过对象调用getClass()方法
        Class stringClass3="".getClass();

        System.out.println(stringClass1.hashCode());
        System.out.println(stringClass2.hashCode());
        System.out.println(stringClass3.hashCode());

    }
}

 2、获取类的名称、包名、构造方法、成员方法方法和成员变量等信息

//获取丰富的类型内容
public class Test04 {
    public static void main(String[] args) throws ClassNotFoundException {

        Class clz=Class.forName("java.util.HashMap");

        //获取类名
        System.out.println("完全限定名:"+clz.getName());
        System.out.println("简单的类名:"+clz.getSimpleName());
        System.out.println();

        //获取包名
        System.out.println("package包名:"+clz.getPackage().getName());
        System.out.println();

        //获取成员变量
        Field[] fieldArray=clz.getDeclaredFields();
        System.out.println("成员变量(字段)");
        for (Field field:fieldArray){
            System.out.println(field);
        }
        System.out.println();

        //获取成员方法
        Method[] methodArray=clz.getDeclaredMethods();
        System.out.println("成员方法");
        for(Method method:methodArray){
            System.out.println(method);
        }
    }
}

3、通过反射的方式,创建对象 

注:当类中没有无参构造方法时,默认一个无参构造方法(不会报错),当我们自定义一个无参构造方法再删除之后,将不会再有默认无参构造方法。

//通过反射的方式,创建对象
public class Test05 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        Class clz = Class.forName("com.xn.test.Document");

        //方式1:直接通过Class对象,调用newInstance()方法
        Object objectx=clz.newInstance();

        //方式2:通过构造器(构造方法)
        //无参构造方法
        Constructor constructor1=clz.getDeclaredConstructor();//获取无参构造方法
        System.out.println(constructor1);
        Object object1=constructor1.newInstance();//执行构造器(构造方法),创建对象

        //有参构造方法
        Constructor constructor2=clz.getDeclaredConstructor(String.class);//获取有参构造方法
        System.out.println(constructor2);
        Object object2=constructor2.newInstance("番茄炒蛋");

        Constructor constructor3=clz.getDeclaredConstructor(int.class);//获取有参构造方法
        System.out.println(constructor3);
        Object object3=constructor3.newInstance(55);

        Constructor constructor4=clz.getDeclaredConstructor(String.class,int.class);//获取有参构造方法
        System.out.println(constructor4);
        Object object4=constructor4.newInstance("豆浆油条",88);

        System.out.println(objectx);
        System.out.println(object1);
        System.out.println(object2);
        System.out.println(object3);
        System.out.println(object4);
    }
}

三、调用方法 

 4、通过反射机制获取构造器信息

  • 获取公共构造器:只获取public修饰的构造器
  • 获取所有构造器(包括私有构造器):获取所有构造器(包括private)
  • 获取指定构造器获取一个接受 指定类型参数的构造器
  • 调用私有构造器通过调用 setAccessible(true) 方法,允许访问私有构造器。
public class Test06 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class clz = Class.forName("com.xn.test.Document");

        //获取一组构造器
        Constructor[] constructorArray1 = clz.getConstructors();
        Constructor[] constructorArray2 = clz.getDeclaredConstructors();

        //获取指定构造器
        Constructor constructor = clz.getDeclaredConstructor(String.class);
        System.out.println(constructor);

        //调用私有构造器,必须设置它的访问权限
        constructor.setAccessible(true);

        //调用构造器,创建对象
        Object object = constructor.newInstance("长安三万里");
        System.out.println(object);
    }
}

5、通过反射,访问并使用成员变量

//通过反射,访问并使用成员变量
public class Test07 {
    public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException, ClassNotFoundException, InstantiationException {

        //硬编码的方式
//        Document doc1  = new Document();
//        doc1.name="";
//        doc1.FavCount=45;

        //反射的方式
        Class clz = Class.forName("com.xn.test.Document");//获取类型信息
        Object obj = clz.newInstance();

        //获取指定名称的成员变量
        Field nameField = clz.getDeclaredField("name");
        Field favCountField = clz.getDeclaredField("favCount");

        //访问私有的成员变量,必须设置权限
        nameField.setAccessible(true);
        favCountField.setAccessible(true);

        //使用成员变量,将指定数据存入对象中
        nameField.set(obj, "喜马拉雅");
        favCountField.setInt(obj, 9897);

        System.out.println(obj);
    }
}

6、使用 Java 反射机制调用对象的方法

public class Test08 {
    private Object doc1;

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        //硬编码的方式
//        Document doc1=new Document();
//        doc1.setName("海底两万里");
//        doc1.setFavCount(20000);

        //反射的方式
        Class clz = Class.forName("com.xn.test.Document");//获取类型信息
        Object doc1 = clz.newInstance();

        //获取指定名称和参数类型的方法
        Method setNameMethod = clz.getMethod("setName", String.class);
        Method setFavCountMethod = clz.getMethod("setFavCount", int.class);

        //执行方法
        //doc1.setName("海底两万里");
        setNameMethod.invoke(doc1, "海底两万里");

        // doc1.setFavCount(20000);
        setFavCountMethod.invoke(doc1, 20000);

        System.out.println(doc1);

    }
}

 7、使用 Java 反射机制调用静态方法

public class Test09 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException {
        //硬编码的方式
        //调用静态方法
//        Document.dosth();
        String ret1 = String.format("HashMap的默认初始容量是%d,加载因子默认是%.2f", 16, 0.75f);
//        System.out.println(ret1);

        //反射的方式
//        Class clz=Class.forName("com.xn.Document");
//        Method dosthMethod=clz.getMethod("dosth");
//        dosthMethod.invoke(null);

        Class clz = String.class;
        Method formatMethod = clz.getMethod("format", String.class, Object[].class);
        String ret2 = formatMethod.invoke(null, "HashMap的默认初始容量是%d,加载因子默认是%.2f", new Object[]{16, 0.75f}).toString();
        System.out.println(ret2);
    }
}

        案例 

  

#sql=select * from subject
#sql=SELECT id,city_name_pinyin AS cityNamePinyin,city_name AS cityName,first_char AS firstChar,is_hot AS isHot from city
sql=SELECT id,title ,url ,playable ,is_new AS isNew,rate ,cover,type  from subject

创建SQLHandler 的类,它用于执行 SQL 查询并将结果映射到 Java 对象中。代码中使用了 JDBC 连接数据库、PreparedStatement 执行 SQL 语句以及反射机制来创建结果对象。 

//SQL执行器
public class SQLHandler {
    private static final String JDBC_URL = "jdbc:mysql://localhost:3306/mybatis_demo?serverTimezone=GMT";
    private static final String DB_USER = "root";
    private static final String DB_PASS = "123456";

    public static <T> List<T> execute(String sql, Class<T> resultType) throws NoSuchFieldException,SecurityException{

        try {
            //创建Connection连接对象
            Connection con = DriverManager.getConnection(JDBC_URL, DB_USER, DB_PASS);

            //创建PreparedStatement执行对象
            PreparedStatement pst = con.prepareStatement(sql);

            //执行sql语句,获取结果集
            ResultSet rs = pst.executeQuery();

            //获取结果集的字段名称
            ResultSetMetaData resultSetMetaData=rs.getMetaData();

            //创建一个用于保存查询结果的list集合
            List<T> queryList = new ArrayList<T>();

            while (rs.next()) {
                //每行数据,对应一个数据对象(反射凡是创建)
                Object resultData = resultType.newInstance();

                //按照字段的顺序,获取字段别名
                for(int i=1;i<=resultSetMetaData.getColumnCount();i++){
                    //获取当前字段名
                    String columnLabel=resultSetMetaData.getColumnLabel(i);

                    //根据字段名称,获取成员变量对象
                    Field field=resultType.getDeclaredField(columnLabel);
                    field.setAccessible(true);

                    //判断当前字段的类型
                    //根据字段名称,虎丘当前行中的指定数据
                    switch (resultSetMetaData.getColumnType(i)){
                        case Types.INTEGER:
                            field.setInt(resultData,rs.getInt(columnLabel));
                            break;
                        case Types.DOUBLE:
                            field.setDouble(resultData,rs.getDouble(columnLabel));
                            break;
                        default:
                            String value=rs.getString(columnLabel);
                            if("true".equals(value)||"false".equals(value)){
                                field.setBoolean(resultData,Boolean.valueOf(value));
                            }else {
                                field.set(resultData,value);
                            }
                    }
                }

                queryList.add((T) resultData);
            }
            return queryList;
        } catch (InstantiationException e) {
            e.printStackTrace();
            return Arrays.asList();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            return Arrays.asList();
        } catch (SQLException e) {
            e.printStackTrace();
            return Arrays.asList();
        }
    }

}

 Test测试类

public class Test {
    public static void main(String[] args) throws IOException, NoSuchFieldException ,SecurityException{
        //创建properties对象
        Properties props=new Properties();

        //读取执行的配置文件
        InputStream in=Test.class.getResourceAsStream("sql.properties");
        props.load(in);

        String sql=props.getProperty("sql");
        System.out.println(sql);



        List<Subject> lsubject= SQLHandler.execute(sql, Subject.class);
        System.out.println("查询结果数量:"+lsubject.size());
        for (Subject subject:lsubject) {
            System.out.println(subject);
        }

//        List<City> lcity = SQLHandler.execute(sql, City.class);
//        System.out.println("查询结果数量:" + lcity.size());
//        for(City city : lcity) {
//        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值