Java 的反射机制

目录

 

一、什么是反射?

二、反射的原理和过程

2.1 Class 对象

2.2 获得 Class 对象的方法(三种方法)

1. getClass()

2. 任何数据类型(包括基本的数据类型)都有一个“静态”的class属性

3. 通过class类的静态方法:forName(String className)(最常用)

2.3 获取Class对象后,如何使用

2.3.1 利用Class对象获取对应的类的各种信息

2.3.2 利用Class对象创建实例

2.3.3 利用Class对象调用具体方法

2.3.4 反射方法的其他使用--通过反射运行配置文件内容

三、反射的优缺点

优点:

缺点:


一、什么是反射?

1,JAVA反射机制是在运行状态中

对于任意一个类,都能够知道这个类的所有属性和方法;

对于任意一个对象,都能够调用它的任意一个方法和属性;

这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

2,反射提供的功能:

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法

(要想解剖一个类,必须先要获取到该类的字节码文件对象(class对象)。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.)

 

二、反射的原理和过程

1. 当我们编写完一个类的代码(例如 Student.java),并进行编译时,会产生 Student.class 字节码文件。里面记录着Student类的信息。

2. 当代码 new 一个类时,例如 new Student()。JVM会首先从磁盘中查找Student.class文件,然后利用Student.class 生成一个关于Student的Class对象,在JVM中,每个类都有且只有一个对应的 Class对象保存在内存。当要 new 一个Student实例对象时,就会利用Student对应的Class对象来创建Student实例对象。

3. 因此,上面过程让我们知道了 Class对象 是保留了某个类的所有信息时,我们就可以通过 得到某个类的Class 对象,来获取该类的所有信息,包括方法,字段 等等。

 

2.1 Class 对象

什么是Class对象?

Class对象并不是 new 出来的实例对象。

在Java中,用来表示 运行时类型信息的的对应类 就是 Class类。Class类是真真切切存在的,它就在 java.lang包里,路径是 java.lang.Class。Class对象 表示的是自己手动编写类的类型信息,比如创建一个Shapes类,那么,JVM就会创建一个Shapes对应Class类的Class对象,该Class对象保存了Shapes类相关的类型信息。

实际上在Java中,每一个类都有一个Class对象,每当我们编写并编译新创建的类,就会产生一个对应Class对象并且这个Class对象会被保存在 同名的.class文件中(编译后的字节码文件保存的就是Class对象)。

那我们为什么需要这样一个Class对象呢?

当我们new一个新对象时,JVM的类加载器会将对应的Class对象加载到JVM中,然后JVM再根据这个类型信息相关的Class对象创建我们需要的实例对象。

值得注意的是:手动编写的每个class类,无论创建多少个实例对象,在JVM中都只有一个Class对象,即在内存中每个类有且只有一个相对应的Class对象。通俗点说就是 .class文件加载进内存后,JVM会自动创建一个Class对象,一个类只会产生一个Class对象(具体可阅读双亲委派模型)。

JVM加载类 和 反射机制的过程如下图:

 

2.2 获得 Class 对象的方法(三种方法)

1. getClass()

		//第一种方式获取Class对象  
		Student stu1 = new Student();//这一new 产生一个Student对象,一个Class对象。
		Class stuClass = stu1.getClass();//获取Class对象

2. 任何数据类型(包括基本的数据类型)都有一个“静态”的class属性

		//第二种方式获取Class对象
		Class stuClass2 = Student.class;

3. 通过class类的静态方法:forName(String className)(最常用)

通过类所在的路径来获取 Class

//第三种方式获取Class对象
		try {
			Class stuClass3 = Class.forName("fanshe.Student");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
			System.out.println(stuClass3 == stuClass2);//判断三种方式是否获取的是同一个Class对象
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

在上面三种方式中,最常用的是第三种,因为:

第一种:第一种对象都有了还要反射干什么。

第二种:需要导入类包,依赖太强。(导入Student类包)

第三种:一般都使用第三种,只要通过一个类的路径就可以获取该类的Class对象。 一个字符串可以传入也可以写在配置文件中等多种方法。这样做解耦性比较强(路径写在配置文件)。

 

2.3 获取Class对象后,如何使用

2.3.1 利用Class对象获取对应的类的各种信息

public class teext {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, ClassNotFoundException {
        String a = "java.lang.String";
        // 根据一类的全名字符串来获得一个类的类对象
        Class<?> clazz = Class.forName(a);
        System.out.println("========================");
        // 获得传递过来的类的所有方法
        Method[] methods = clazz.getDeclaredMethods();
        // 打印 类的所有方法
        for (Method m : methods) {
            System.out.println(m);
        }
        System.out.println("========================");
        // 打印 类的所有属性变量
        Field[] fields = clazz.getDeclaredFields();
        for (Field f:fields){
            System.out.println(f);
        }
        System.out.println("========================");
        // 打印 类的所有构造器
        Constructor[] constructors = clazz.getDeclaredConstructors();
        for(Constructor c : constructors){
            System.out.println(c);
        }
    }
}

2.3.2 利用Class对象创建实例

public class teext {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, ClassNotFoundException {
        String a = "java.lang.String";
        //获取String的Class对象
        Class<?> c = Class.forName(a);
        //通过Class对象获取指定的Constructor构造器对象,该构造器是有一个String形参的
        Constructor constructor=c.getConstructor(String.class);
        //根据构造器创建实例:
        Object obj = constructor.newInstance("hello reflection");
        System.out.println(obj); //打印hello reflection
    }
}

(1)批量获取的方法:
public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)

(2)单个获取的方法,并调用:
public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;

2.3.3 利用Class对象调用具体方法

        String a = "java.lang.String";
        //获取String的Class对象
        Class<?> c = Class.forName(a);
        //通过Class对象获取指定的Constructor构造器对象,该构造器是有一个String形参的
        Constructor constructor=c.getConstructor(String.class);
        //根据构造器创建实例:
        Object obj = constructor.newInstance("hello reflection");
        //获取指定的charAt方法
        Method m = c.getMethod("charAt",int.class);
        //利用 invoke() 调用方法,obj是指定调用方法的对象,2是给charAt()的形参
        //如果反射的是静态方法,那么obj就要是 null 
        Object result = m.invoke(obj,2);
        System.out.println(result);

2.3.4 反射方法的其他使用--通过反射运行配置文件内容

Student类:

public class Student {
	public void show(){
		System.out.println("is show()");
	}
}

配置文件以txt文件为例子:

className = cn.fanshe.Student
methodName = show

测试类:

/*
 * 我们利用反射和配置文件,可以使:应用程序更新时,对源码无需进行任何修改
 * 我们只需要将新类发送给客户端,并修改配置文件即可
 */
public class Demo {
	public static void main(String[] args) throws Exception {
		//通过反射获取Class对象
		Class stuClass = Class.forName(getValue("className"));//"cn.fanshe.Student"
		//2获取show()方法
		Method m = stuClass.getMethod(getValue("methodName"));//show
		//3.调用show()方法
		m.invoke(stuClass.getConstructor().newInstance());
		
	}
	
	//此方法接收一个key,在配置文件中获取相应的value
	public static String getValue(String key) throws IOException{
		Properties pro = new Properties();//获取配置文件的对象
		FileReader in = new FileReader("pro.txt");//获取输入流
		pro.load(in);//将流加载到配置文件对象中
		in.close();
		return pro.getProperty(key);//返回根据key获取的value值
	}
}

三、反射的优缺点

优点:

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

缺点:

1. 性能问题,反射机制的性能比直接代码要差

2. 由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用。(反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值