Java反射机制理解

在学习Java反射机制,首先应该了解:

1、什么是反射?
2、为什么会有反射的存在?	
3、用到反射有哪些需要了解?
4、使用反射有哪些方式?
5、常见的API有哪些?

1、什么是反射?
百度百科的解释:JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法功能称为java语言的反射机制。

看到这里应该有点了解,它是一个工具,可以动态获取对象的方法或者属性,不管是类的私有方法还是类的共有方法,都可以在程序运行时动态的获取到。

2、为什么会有反射的存在?
一句话概括就是使用反射可以赋予jvm 动态编译的能力,否则类的元数据信息只能用静态编译的方式实现。

Java中编译类型有两种:

  • 静态编译:在编译时确定类型,绑定对象即通过。
  • 动态编译:运行时确定类型,绑定对象。动态编译最大限度地发挥了Java的灵活性,体现了多态的应用,可以降低类之间的耦合性。

反射可以在运行时加载、探知、使用编译时时期完全未知的classes。即Java程序可以加载一个运行时才得知名称的class,获取其完整构造,并生成其对象实体、或对其fields设值、调用它的methods。 反射允许静态语言在运行时(runtime)检查、修改程序的结构与行为。在静态语言中,使用一个变量时,必须知道它的类型。在Java中,变量的类型信息在编译时期都保存了class文件中,这样运行时才能保证准确无误。换句话说,程序在运行时的行为都是固定的,如果想在运行时改变,就需要反射这个东西。

3、用到反射有哪些需要了解?
实现Java反射机制的类都位于Java.lang.reflect包中:

  • Class类:代表一个类
  • Field类:代表类的成员变量
  • Method类:代表类的方法
  • Constructor类:代表类的构造方法
  • Array类:提供了动态创建数组,以及访问数组的元素的静态方法

可以通过动态的方式获取上面的各种参数,如:私有/公有的字段、私有/共有的方法、构造器等。

4、使用反射有哪些方式?
使用反射主要有3种方式:
方式一:

package FanSheTest;

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

/*
 * 测试类的反射
 * */

class A {
	private String name ;
	public int age ;
	public String home ;

	private void say() {
		System.out.println("私有方法say()成功调用!");
	}

	public void read() {
		System.out.println("公有方法read()成功调用!");
	}	

	public A() {
		System.out.println("构造器执行了!");
	}
}
public class Test {

	public static void main(String[] args) {
		try {
			// 方式一:很普通的使用new进行对象的创建
			/*
			 * A a = new A(); 
			 * Class clazz = a.getClass();
			 */
			 
			// 方式二:
			/*
			Class clazz = A.class;
			*/ 
			
			// 方式三:使用Class.forName("类的全命名")
			Class clazz = Class.forName("FanSheTest.A");
		
			
			// 获取该类的所有字段,包括私有的字段
			Field[] f = clazz.getDeclaredFields();
			//获取该类的所有方法,包含私有的方法
			Method[] method = clazz.getDeclaredMethods();
			// 获取所有的public字段
			for (Field field : f) {
				System.out.println("含有字段:" + field.getName());
			}
			//获取所有的方法
			for(Method m:method) {
				System.out.println(m);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

运行结果:
	含有字段:name
	含有字段:age
	含有字段:home
	public void FanSheTest.A.read()
	private void FanSheTest.A.say()

使用反射的方法主要有上面3种方式,常用的就是第三种,通过Class.forName(“类全名”)获取对象。

5、常见的API有哪些?

常见的反射获取对象的API主要有以下几类:

  • 获取字段
  • 获取方法
  • 获取构造器

获取字段:

//获取所有的public字段
Field[] field = clazz.getFields();
//获取所有的字段,包含private修饰的字段
Filed[] field = clazz.getDeclaredFields();

获取方法:

//获取所有的public方法
Method[] field = clazz.getMethods();
//获取所有的字段,包含private修饰的方法
Method[] field = clazz.getDeclaredMethods();

单独获取某个方法的调用:

//调用read方法,如果该方法有参数,则在后面继续添加方法的类型//clazz.getDeclaredMethod("read",String.class,....)
Method m1 = clazz.getDeclaredMethod("read");
//调用invoke方法执行
m1.invoke(clazz.newInstance());

获取构造器:

//获取包括有参构造和无参构造
Constructor[] con = clazz.getConstructors();
 for(Constructor c:con) {				
		System.out.println(c);
	}

使用反射可以跳过泛型的检查

package FanSheTest;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/*
 * 通过反射了解泛型的本质
 * */
public class Test1 {

	public static void main(String[] args) throws InstantiationException {
		List list1 = new ArrayList<>();
		List<String> list2 = new ArrayList<String>();
		
		list1.add("张三");
		list1.add("李四");
		System.out.println(list1.size());
		
		list2.add("abc");
		//list2.add(20);直接会在检查时报错
		System.out.println(list2.size());
		
		Class clazz = list2.getClass();
			Method m2;
			try {
				m2 = clazz.getMethod("add", Object.class);
				m2.invoke(list2, 20);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} 
			System.out.println("--------------使用反射添加-------------------");
			System.out.println("集合大小:"+list2.size());
			
	}
}
运行结果:
		2
		1
		--------------使用反射添加-------------------
		集合大小:2

虽然在定义list2的时候已经规定了泛型参数类型为:String,但是在正常使用添加元素的时候会在检查阶段报错,如果使用反射方式,拿到add方法时,向其中添加int类型的元素时,集合的长度是2,表明已经添加成功;当时当遍历这个集合时,会报java.lang.ClassCastException表明泛型只是在编译时期有效,在运行时期是失效的。

总结:
1、学习反射可以实现对类对象的动态访问,而不只是在静态编译时期
2、反射可以访问对象的私有成员变量和方法,并可以对其进行值的修改操作。
3、反射技术主要是用在框架中,平时开发时很少用到反射技术,所以学习好反射技术是很重要的。

你可能感兴趣的:(Java面试,Java反射深入理解)