Java反射--1
反射
概述
反射机制指的是Java在运行时候有一种自观的能力,能够了解自身的情况为下一步做准备,其想表达的意思就是:
在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。通俗点讲,通过反射,想要获取任何东西都可以,这是一种动态获取类的信息以及动态调用对象方法的能力。
Java提供的反射机制,依赖于Class类和java.lang.reflect类库。
①Class表示类或者接口;
②java.lang.reflect.Field表示类中的成员变量;
③java.lang.reflect.Method表示类中的方法;
④java.lang.reflect.Constructor表示类的构造方法;
⑤Array提供动态数组的创建和访问数组的静态方法。
类加载器在加载阶段(还有连接和初始化)负责根据一个类的全限定名来读取此类的二进制字节流(从.class文件读取)到JVM内部,并存储在运行时内存区的方法区(存的元数据),然后将其转换为一个与目标类型对应的java.lang.Class对象实例(参照OpenJDK1.8的源码,Class对象应该存在于Heap堆中;1.7开始在堆;1.6在方法区)Class对象是存放在堆区的,不是方法区,这点很多人容易犯错。类的元数据(元数据并不是类的Class对象。Class对象是加载的最终产品,类的方法代码,变量名,方法名,访问权限,返回值等等都是在方法区的)才是存在方法区的。
无论创建(new)多少个实例对象,在JVM中都只有一个Class对象,即在内存中每个类有且只有一个相对应的Class对象
获取Class
想要使用反射机制,就必须要先获取到该类的字节码文件对象Class对象(从堆获取),通过该类的字节码对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象。
Class有下面的几个特点
①Class也是类的一种(不同于class,class是一个关键字);
②Class类只有一个私有的构造函数,只有JVM能够创建Class类的实例;
③对于同一类的对象,在JVM中只存在唯一一个对应的Class类实例来描述其信息;
④每个类的实例都会记得自己是由哪个Class实例所生成;
⑤通过Class可以完整的得到一个类中的完整结构;
因为Class只有一个私有的构造函数,所以我们不能通过new创建Class实例 ,有下面三种获取Class对象的方式:
1)、Class.forName(“类的全限定名”)
2)、实例对象.getClass()
3)、类名.class (类字面常量)
例:Person person = new Person();
Class clazz1 = person.getClass(); //1、通过Object类的getClass()方法:(需要先实例化一个对象)
Class clazz2 = person.class; //2、通过对象实例方法获取对象:(需要先实例化一个对象)
Class clazz3 = Class.forName("com.cn.Person"); //3、类的全路径:(不需要实例对象),传入一个路径参数,就能实现运行时的动态获取Class对象
Person person = (Person)clazz3.newInstance();//通过Class对象动态创建实例
反射的意义
反射的意义是什么,其实就是为了代码简洁,提高代码的复用率,外部调用方便,源代码,反编译都能看到。某些情况下解耦用反射要清晰很多,下面是简单的测试
package cn.yonyong.reflection.testdemo; interface Fruit { //水果接口 public void eat() ; //吃水果 } class Apple implements Fruit{ //定义苹果 public void eat() { System.out.println("**吃苹果。"); } } class Orange implements Fruit{ public void eat() { System.out.println("**吃橘子。"); } } class Factory{ public static Fruit getInstance(String className){ Fruit fruit = null ; try{ fruit = (Fruit) Class.forName(className).newInstance() ; }catch(Exception e ){ e.printStackTrace() ; } return fruit ; } } public class FactoryDemo{ public static void main(String args[]){ //通过工厂类取得接口实例,传入完整的包.类名称 Fruit f = Factory.getInstance("cn.yonyong.reflection.testdemo.Apple") ; if(f!=null){ //判断是否取得接口实例 f.eat() ; } } }
输出:
**吃苹果。
如果不用反射,那么我们如果再加一个西瓜类,就得在Factory里判断,每添加一个类都要修改一次Factory,但用了反射只用在调用的时候传入完整的类名就可完成。
结果:用反射完成了动态创建不同的实现类,只需要传入类名
java面向对象主要有四大特性:
封装、抽象、继承和多态。
封装:在面向对象语言中,封装特性是由类来体现的,我们将现实生活中的一类实体定义成类,其中包括属性和行为(在Java中就是方法),就好像人类,可以具有name,sex,age等属性,同时也具有eat(),sleep()等行为,我们在行为中实现一定的功能,也可操作属性,这是面向对象的封装特性;
抽象:抽象就是将一类实体的共同特性抽象出来,封装在一个抽象类中,所以抽象在面向对象语言是由抽象类来体现的。比如鸟就是一个抽象实体,因为抽象实体并不是一个真正的对象,它的属性还不能完全描述一个对象,所以在语言中体现为抽象类不能实例化;
继承:继承就像是我们现实生活中的父子关系,儿子可以遗传父亲的一些特性,在面向对象语言中,就是一个类可以继承另一个类的一些特性,从而可以代码重用,其实继承体现的是is-a关系,父类同子类在本质上还是一类实体;
多态:多态就是通过传递给父类对象引用不同的子类对象从而表现出不同的行为。不同的子实现类,有不同的实现方法。
那么既然Java反射可以访问和修改私有成员变量,那封装成private还有意义么?
既然小偷可以访问和搬走私有成员家具,那封装成防盗门还有意义么?这是一样的道理,并且Java从应用层给我们提供了安全管理机制——安全管理器,每个Java应用都可以拥有自己的安全管理器,它会在运行阶段检查需要保护的资源的访问权限及其它规定的操作权限,保护系统免受恶意操作攻击,以达到系统的安全策略。所以其实反射在使用时,内部有安全控制,如果安全设置禁止了这些,那么反射机制就无法访问私有成员。