JavaSE基础:反射机制

JavaSE基础:反射机制

前言

以下知识点讲解反射机制

一、基础概念

1、反射机制的定义

	通过java语言中的反射机制可以操作字节码文件。
	优点类似于黑客。(可以读和修改字节码文件。)
	通过反射机制可以操作代码片段。(class文件。)
	可以让程序更加灵活。

2、反射机制相关的类在哪个包下?

	java.lang.reflect.*;

3、反射机制相关的主要的类

	java.lang.Class
	java.lang.reflect.Method;
	java.lang.reflect.Constructor;
	java.lang.reflect.Field;

4、在java中获取Class的三种方式

	第一种:	 
		Class c = Class.forName("完整类名");
	第二种:
		Class c = 对象.getClass();
	第三种:
		Class c = int.class;
		Class c = String.class;

5、获取了Class之后,可以调用无参数构造方法来实例化对象

	//c代表的就是日期Date类型
	Class c = Class.forName("java.util.Date");

	//实例化一个Date日期类型的对象
	Object obj = c.newInstance();

	一定要注意:
		newInstance()底层调用的是该类型的无参数构造方法。
		如果没有这个无参数构造方法会出现"实例化"异常。

6、如果只想让一个类的“静态代码块”执行的话,可以怎么做?

	Class.forName("该类的类名");
	这样类就加载,类加载的时候,静态代码块执行!!!!
	在这里,对该方法的返回值不感兴趣,主要是为了使用“类加载”这个动作。

二、基础练习例子

1、获取Class的三种方式

/*
反射机制:可以操作字节码文件
		作用:可以让程序更加灵活。
		java.lang.reflect.*;

		java.lang.Class
		java.lang.reflect.Method;
		java.lang.reflect.Constructor;
		java.lang.reflect.Field;
在java中获取Class的三种方式?
       第一种:
           Class c = Class.forName("完整类名");
       第二种:
           Class c = 对象.getClass();
       第三种:
           Class c = int.class;
           Class c = String.class;
 */
 import java.util.Date;

public class ReflectTest01 {
    public static void main(String[] args) throws Exception{
        // 第一种方式:Class c = Class.forname("完整类名")
        Class c1 = Class.forName("com.lin.ReflectBasic.Basic.User");
        Class c2 = Class.forName("java.lang.String");
        Class c3 = Class.forName("java.util.Date");
        // 第二种方式:【属于Object中的一个方法,每个对象都有的】
        // Class c = 对象.getClass();
        String s = "abc";
        Class x = s.getClass();
        // 判断c2和x内存地址是否相同
        System.out.println(c2 == x); // true
        Date time = new Date();
        Class y = time.getClass();
        //判断c3和y内存地址是否相同
        System.out.println(c3 == y); // true
        // 第三种方式:java中每个数据类型中能调用.class
        Class z = String.class;
        // 判断c2和z的内存地址是否相同
        System.out.println(z == c2); // true
    }
}

2、使用反射机制实例化对象

/*
获取了Class之后,可以调用无参数构造方法来实例化对象

		//c代表的就是日期Date类型
		Class c = Class.forName("java.util.Date");

		//实例化一个Date日期类型的对象
		Object obj = c.newInstance();

		一定要注意:
			newInstance()底层调用的是该类型的无参数构造方法。
			如果没有这个无参数构造方法会出现"实例化"异常。
 */
public class ReflectTest02 {
    public static void main(String[] args) {
        // 已过时的方法 newInstance()
        try {
            Class c = Class.forName("com.lin.ReflectBasic.Basic.User");
            // 实例化
            Object obj = c.newInstance();
            System.out.println(obj);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        User user = new User();
        System.out.println(user);
    }
}

3、验证反射机制的灵活性

提前准备的一个类

/*提前准备的一个类*/
public class User {
    // Field
    private String name;
    private int no;
    // Constructor
    public User() {
        System.out.println("无参构造方法调用!");
    }
    public User(String name, int no) {
        this.name = name;
        this.no = no;
    }
    // Method
    public void dosome(){
        System.out.println("dosome");
    }

    public String getName() {
        return name;
    }

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

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }
}

提前准备的配置文件

classname = com.lin.ReflectBasic.Basic.User
/*
验证反射机制的灵活性
    在不改变java代码源代码的基础之上,可以做到不同对象的实例化
    非常灵活(符合OCP开闭原则:对外扩展开放,对修改关闭)
后期学习的高级框架工作中要使用
包括 SSH SSM
    Spring SpringMVC MyBatis
    Spring Struts Hibernate
    ...
    这些框架底层的实现原理都采用了反射机制
 */
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;

public class ReflectTest03 {
    public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
        // 通过IO流读取classinfo.properties文件
        FileReader fileReader = new FileReader("Java_Reflect/classinfo.properties");
        // 创建属性类对象Map
        Properties properties = new Properties();
        // 加载
        properties.load(fileReader);
        // 关闭流
        fileReader.close();
        // 通过key获取value
        String classname = properties.getProperty("classname");
        // 实例化对象
        Class c = Class.forName(classname);
        Object object = c.newInstance();
        System.out.println(object);
    }
}

4、关于路径问题

/*
关于路径名问题
    String path = Thread.currentThread().getContextClassLoader()
                      .getResource("写相对路径,但是这个相对路径从src出发开始找").getPath();

    String path = Thread.currentThread().getContextClassLoader()
                      .getResource("abc").getPath();	//必须保证src下有abc文件。

    String path = Thread.currentThread().getContextClassLoader()
                      .getResource("a/db").getPath();	//必须保证src下有a目录,a目录下有db文件。

    String path = Thread.currentThread().getContextClassLoader()
                      .getResource("com/bjpowernode/test.properties").getPath();
                      //必须保证src下有com目录,com目录下有bjpowernode目录。
                      //bjpowernode目录下有test.properties文件。

    这种方式是为了获取一个文件的绝对路径。(通用方式,不会受到环境移植的影响。)
    但是该文件要求放在类路径下,换句话说:也就是放到src下面。
    src下是类的根路径。

    直接以流的形式返回:
    InputStream in = Thread.currentThread().getContextClassLoader()
                            .getResourceAsStream("com/bjpowernode/test.properties");
 */

import java.io.FileReader;
import java.util.Properties;

public class AboutPath {
    public static void main(String[] args) throws Exception {
        // 使用IO流classinfo.properties文件
        // 使用这种方法的路径缺点:移植性差,在IDEA中默认的当前路径是project的根
        // 这个方法离开了IDEA换到了其他位置可能当前路径就不是project的根
        //FileReader fileReader = new FileReader("");

        // 通用的方法:即使代码换了位置【到了linux系统等】编写仍然是通用的
        // 使用此方法的前提是这类必须在根路径下
        // 什么是类路径下?即src是类的根路径
        String path = Thread.currentThread().getContextClassLoader()
                .getResource("classinfo2.properties").getPath();
        FileReader fileReader = new FileReader(path);
        Properties properties = new Properties();
        properties.load(fileReader);
        fileReader.close();
        String className = properties.getProperty("classname");
        System.out.println(className);
    }
}


5、资源绑定器(ResourceBundle)

提前准备的配置文件

classname=com.lin.ReflectBasic.Basic.User
/*
资源绑定器(java.util.ResourceBundle)
    public abstract class ResourceBundle
        extends Object

    便于获取配置文件中的内容
    只能绑定xxx.properties文件,并且这个文件必须在类路径下,文件的扩展名也必须是.properties
    并且在写路径的时候,路径后面的扩展名不能写。

    static ResourceBundle getBundle(String baseName)
    使用指定的基本名称,默认语言环境和调用者的类加载器获取资源包。
 */
import java.util.ResourceBundle;

public class ResourceBundleTest {
    public static void main(String[] args) {
        // 获取calssinfo2资源包
        ResourceBundle resourceBundle = ResourceBundle.getBundle("classinfo2");
        String className = resourceBundle.getString("classname");
        System.out.println(className);
    }
}

6、安全性

在处理反射时安全性是一个较复杂的问题。反射经常由框架型代码使用,由于这一点,我们可
能希望框架能够全面介入代码,无需考虑常规的介入限制。但是,在其它情况下,不受控制的
介入会带来严重的安全性风险,例如当代码在不值得信任的代码共享的环境中运行时。

7、反射缺点

反射是一种强大的工具,但也存在一些不足。

性能问题。使用反射基本上是一种解释操作,我们可以告诉 JVM,我们希望做什么并且它满足我们的要求。用于字段和方法接入时反射要远慢于直接代码。性能问题的程度取决于程序中是如何使用反射的。如果它作为程序运行中相对很少涉及的部分,缓慢的性能将不会是一个问题。

使用反射会模糊程序内部实际要发生的事情。程序人员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术会带来维护问题。反射代码比相应的直接代码更复杂。
解决这些问题的最佳方案是保守地使用反射——仅在它可以真正增加灵活性的地方——记录其在目标类中的使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值