【详细】关于Java中的反射

前提,开始序号为2是因为这里摘自我的笔记
2.对象有编译类型和运行类型
	Object obj = new java.util.Date();
编译类型:Object
运行类型(obj对象真实的类型):java.util.Date
需求:通过obj对象来调用java.util.Date类中的toLocaleString方法
obj.toLocalString() //编译错误
因为toLocalString方法不在Object类中
解决方法:
	java.util.Date d = (java.util.Date)obj;
	d.toLocalString();
新的问题:
	但,如果不知道对象的真实类型,无法做强制类型转换
	特别是,当某一个对象通过方法返回,看不到底层
怎么办?->反射
这里的问题的解答在下面的第13点
	
3.类也是一种对象
类:描述对象
Class:描述类

那么,Class类和Object类有什么关系?
	Object:表示一切对象
	Class:表示一切类

4.到底,什么是反射?
	在运行时期,动态的去获取类中的成员信息
(包、父类、接口、修饰符、类名、构造器、字段、方法等)
Eclipse中的outline(大纲视图)就是通过反射编写的

反射的API:
	Class:描述一切的类
	Constructor:描述一切的构造器
	Method:描述一切的方法
	
使用反射:创建对象,调用方法

然而,反射操作性能较低。
但是-->Java中的框架都是基于反射编写

5.Class类与Class实例
Class类	 :用来表示所有的类,是所有类的类型
Class实例:表示在JVM中运行的一份份字节码文件

Class类可以表示所有类,那如何明确是具体哪个类呢?
由此,SUN公司提高了一个泛型,用来表示当前所表示的是哪一个类
	若当前描述String类:	Class<String> clz
	若当前描述ArrayList类:	Class<ArrayList> clz
表示Class对象的三种方式
	1.类型.class	表示一份字节码
	2.对象.getClass()	获取当前对象的真实类型
	3.使用Class.forName(String className)	根据类的全限定名来获取Class对象
	
	Class<java.util.Date> clz1 = java.util.Date.class;

	java.util.Date date = new java.util.Date();
	Class clz2 = date.getClass();

	Class clz3 = Class.forName("java.util.Date");
	
	System.out.println(clz1 + "\r\n" + clz2 + "\r\n" + clz3);
	/*class java.util.Date
	  class java.util.Date
	  class java.util.Date */
	 
6.九大内置的Class实例和数据的Class实例
8大基本数据类型和void关键字都可以表示为Class的实例
	如:Class intClz = int.class;
		boolean blClz = boolean.class;
	
	问题:int和Integer是同一数据类型吗?
	System.out.println(int.class == Integer.class);
	//false
	
	在包装类中都有一个TYPE常量,返回对应的基本类型的Class对象
	System.out.println(int.class == Integer.TYPE); 
	//true

表示数组的Class对象
	方式1:数组名.getClass();
	Class clz1 = arr.getClass();
	方式2:数组类型.class;
	Class clz2 = int[].class;

9.获取构造器
Class类描述了所有的类的成员(构造函数、方法等)
——>Class类具有获取某一个类的构造函数的方法
步骤:1)获取被操作的类的字节码对象
	  2)获取构造函数

获取构造器的方法:
Constructor<?>[] getConstructors() 返回包含一个数组 Constructor对象反射由此表示的类的所有公共构造 类对象 
Constructor<?>[] getDeclaredConstructors() 返回一个反映 Constructor对象表示的类声明的所有 Constructor对象的类  

Constructor<T> getConstructor(类<?>... parameterTypes) 返回一个 Constructor对象,该对象反映 Constructor对象表示的类的指定的公共类函数 
Constructor<T> getDeclaredConstructor(类<?>... parameterTypes) 返回一个 Constructor对象,该对象反映 Constructor对象表示的类或接口的指定 类函数。 

10.调用构造器创建对象
T newInstance(Object... initargs) 
使用此 Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。 

通过构造器对象使用newInstance可以获得实例
	c = clz.getConstructor(String.class);
	obj = c.newInstance("公有、有参数");
	
但是,对于私有的构造器,会有“非法访问”的异常
	c = clz.getDeclaredConstructor(String.class, int.class);
	//obj = c.newInstance("私有", 1);
	/*
	 * Exception in thread "main" java.lang.IllegalAccessException
	 * ...
	 * can not access a member of class pers.honhong._02_createinstance.User with modifiers "private"
	 */
这里,需要使用setAccessible方法关闭安全检查
void setAccessible(boolean flag) 
将此对象的 accessible标志设置为指示的布尔值。
	c.setAccessible(true);
	obj = c.newInstance("私有", 1);
	
11.获取类中的方法
|--获取所有方法
Method[] getMethods() 
返回包含一个数组方法对象反射由此表示的类或接口的所有【公共】方法类对象
包括那些由类或接口和那些从超类和超接口继承的声明

Method[] getDeclaredMethods() 
返回包含一个数组方法对象反射的类或接口的所有声明的方法,通过此表示类对象
包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。
	//获取User类中所有public的方法(包括继承)
	Method[] methods = clz.getMethods();
	for(Method method : methods) {
		System.out.println(method);
	}
	
	//获取User类中所有的方法(不包括继承)
	methods = clz.getDeclaredMethods();
	for (Method method : methods) {
		System.out.println(method);
	}
|--获取单个方法
Method getMethod(String name, 类<?>... parameterTypes) 
返回一个方法对象它反映此表示的类或接口的指定公共成员方法 类对象 
	String name:方法名
	类<?>... parameterTypes:参数的Class类型
	//获取public void fun1()
	Method method = clz.getMethod("fun1");
	System.out.println(method);
	
	//获取public void fun2(String name)
	method = clz.getMethod("fun2", String.class);
	System.out.println(method);
	
	//获取private void fun3(String name, int age)
	method = clz.getDeclaredMethod("fun3", String.class, int.class);
	System.out.println(method);
	
12.使用反射调用方法
Object invoke(Object obj, Object... args) 
在具有指定参数的Method对象上调用此Method对象表示的底层方法。 
参数 
obj - 从底层方法被调用的对象 
args - 用于方法调用的参数

	//调用方法public void fun1()
	Method m = clz.getMethod("fun1");
	m.invoke(clz.newInstance());	
	//public void fun1()
	
	//调用方法public void fun2(String name)
	m = clz.getMethod("fun2", String.class);
	m.invoke(clz.newInstance(), "公有、有参数");	
	//public void fun2(String name)
	
但是,对于私有的方法,即便我们可以通过getDeclaredMethod方法获取,
我们也没有权限使用。
于是,需要使用java.lang.reflect.AccessibleObject中的setAccessible方法
关闭安全检查
	//调用方法private void fun3(String name, int age)
	m = clz.getDeclaredMethod("fun3", String.class, int.class);
	m.setAccessible(true);
	m.invoke(clz.newInstance(), "私有", 1);
	//private void fun3(String name, int age)
	
13.解答第2点留下的问题,获得某一个以Object为编译类型(不知道运行类型)的对象时,
如何使用它的运行类型所特有的方法
	//现在有一个日期对象,其编译类型是Object
	Object o = new java.util.Date();
	//现在不通过强转调用Date类中的toLocaleString方法
	//利用反射
	Method m = o.getClass().getMethod("toLocaleString");
	System.out.println(m.invoke(o));

14.使用反射调用静态方法和可变数组参数
	如果底层方法是静态的,则可以忽略指定的obj参数,传入null!
	例如:
	//调用public static void staticMethod1()
	Method m = c.getMethod("staticMethod1");
	m.invoke(null);
	//输出:public static void staticMethod1()
	
	如果底层方法是可变数组参数,则分为:
	基本数据类型
	例如:
	//调用public static void staticMethod2(int ... arr)
	m = c.getMethod("staticMethod2", int[].class);
	//m.invoke(null, 1, 2, 3);	运行时错误
	//这里需要把1,2,3封装成数组
	m.invoke(null, new int[]{1, 2, 3});
	//输出:[1, 2, 3]
	
	引用数据类型
	//调用public static void staticMethod3(String ... str_arr)
	m = c.getMethod("staticMethod3", String[].class);
	//m.invoke(null, new String[]{"a", "b", "c"});//运行时错误
	这里,为什么会出现错误呢?
	查看APIdoc,invoke方法下有这么一句话:
	个别参数自动解包以匹配原始形式参数,原始参考参数和参考参数都需要进行方法调用转换。
	再看看invoke方法的声明:
	public Object invoke(Object obj, Object... args)
	哦,需要使用Obejct的一维数组将其打包
	m.invoke(null, new Object[]{new String[]{"a", "b", "c"}});
	//输出:[a, b, c]
	
	实际,对于其它非引用数据类型数组,也可以自己进行打包,如:
	m.invoke(null, new Object[]{new int[]{1, 2, 3}});
	m.invoke(null, new Object[]{1});
	
15.使用反射操作字段
获取字段
	getField();
	getFields();
设置字段
	void setXX(Object obj, Object value);
	
16.其它API
获取修饰符
	getModifiers();
获取类的全限定名
	getName();
获取类的简单名称
	getSimpleName();

17.单例设计模式
	在应用中某一个类(工具类0),有且只有一个实例0
	
单例模式的写法有多种
编写规则
	1):私有化构造器
	2):在类中创建一个自身对象
	3):向外暴露一个公共的静态方法用于返回该对象
使用反射机制之后,传统的单例设计,不安全
-->建议:使用枚举来做单例
Spring框架创建的对象默认就是单例的
	public enum Util {
		INSTANCE;
	}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值