1,Java反射机制概述
-
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期 借助于Reflection API取得任何的内部信息,并能直接操作任意对象的内 部属性及方法。
-
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个 类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可 以通过这个对象看到类的结构。
-
动态语言 vs 静态语言
-
1、动态语言
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以 被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运 行时代码可以根据某些条件改变自身结构。 主要动态语言:Object-C、C#、JavaScript、PHP、Python、Erlang。
-
2、静态语言
与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、 C++。
-
Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动 态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。 Java的动态性让编程的时候更加灵活!
-
-
Java反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
-
反射相关的主要API
获取包名 类名 clazz.getPackage().getName()//包名 clazz.getSimpleName()//类名 clazz.getName()//完整类名 获取成员变量定义信息 getFields()//获取所有公开的成员变量,包括继承变量 getDeclaredFields()//获取本类定义的成员变量,包括私有,但不包括继承的变量 getField(变量名) getDeclaredField(变量名) 获取构造方法定义信息 getConstructor(参数类型列表)//获取公开的构造方法 getConstructors()//获取所有的公开的构造方法 getDeclaredConstructors()//获取所有的构造方法,包括私有 getDeclaredConstructor(int.class,String.class) 获取方法定义信息 getMethods()//获取所有可见的方法,包括继承的方法 getMethod(方法名,参数类型列表) getDeclaredMethods()//获取本类定义的的方法,包括私有,不包括继承的方法 getDeclaredMethod(方法名,int.class,String.class) 反射新建实例 clazz.newInstance();//执行无参构造创建对象 clazz.newInstance(666,”海绵宝宝”);//执行含参构造创建对象 clazz.getConstructor(int.class,String.class)//获取构造方法 反射调用成员变量 clazz.getDeclaredField(变量名);//获取变量 clazz.setAccessible(true);//使私有成员允许访问 f.set(实例,值);//为指定实例的变量赋值,静态变量,第一参数给null f.get(实例);//访问指定实例变量的值,静态变量,第一参数给null 反射调用成员方法 Method m = Clazz.getDeclaredMethod(方法名,参数类型列表); m.setAccessible(true);//使私有方法允许被调用 m.invoke(实例,参数数据);//让指定实例来执行该方法
-
通过反射调用对象的属性,包括构造器,属性和方法
@Test public void test01() throws Exception { //1,通过Person.class的方式获取Person类的Class对象 Class<Person> personClass = Person.class; //2,通过反射创建带两个参数的构造器得到一个构造器对象 Constructor<Person> constructor = personClass.getConstructor(String.class, String.class); //通过构造器对象newInstance方法创建对应的对象 Person tom = constructor.newInstance("1", "tom"); //查看生成的对象 System.out.println(tom.toString());; //3,通过反射,调用对象的属性和方法 //3.1调用属性 Field id = personClass.getDeclaredField("id"); id.set(tom,"2"); System.out.println(tom); //3.2调用方法 Method show = personClass.getMethod("show"); show.invoke(tom); } class Person{ public String id; private String name; public Person() { } private Person( String name) { this.name = name; } public Person(String id, String name) { this.id = id; this.name = name; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void show(){ System.out.println("我是张三!!"); } private String showPerson(String name){ System.out.println("我的名字是:"+name); return name; } @Override public String toString() { return "Person{" + "id='" + id + '\'' + ", name='" + name + '\'' + '}'; } }
-
通过反射调用对象的私有属性,包括构造器,属性和方法
@Test public void test02() throws Exception{ /** * 通过反射调用对象的私有属性,包括构造器,属性和方法 */ //1,通过Person.class的方式获取Person类的Class对象 Class<Person> personClass = Person.class; //2,调用私有构造器方法生成对应对象 Constructor<Person> declaredConstructor = personClass.getDeclaredConstructor(String.class); //3,设置declaredConstructor对象可以对Person类的私有资源进行操作 declaredConstructor.setAccessible(true); //4,调用对应方法创建对象 Person jack = declaredConstructor.newInstance("jack"); System.out.println(jack); //5,调用Person类的私有属性 Field name = personClass.getDeclaredField("name"); name.setAccessible(true); name.set(jack,"jack001"); System.out.println(jack); //6,调用Person类的私有方法得到对应私有方法的对象 Method showPerson = personClass.getDeclaredMethod("showPerson",String.class); showPerson.setAccessible(true); showPerson.invoke(jack,"jack001"); String jack001 = (String)showPerson.invoke(jack, "jack001"); System.out.println(jack001); }
2, 理解Class类并获取Class的实例
1,Class 类
- 获取Class 类对象的三种方式
- Class<?> clazz1 = Class.forName(“类名绝对路径”)
- Class<?> clazz2 = 类名.class
- Class<?> clazz3 = new 类名().getClass()
- 类的加载过程
- 程序经过javac.exe命令编译为字节码文件(.class)
- 通过java.exe对字节码文件进行解释运行,相当于将字节码文件加载到内存中,将字节码文件加载到内存中的过程就称为类的加载过程
- 加载到内存中的类,我们称为运行时类,此运行时类就作为Class类的实例
- 哪些类型可以有Class对象
- class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
- interface:接口
- []:数组
- enum:枚举
- annotation:注解@interface
- primitive type:基本数据类型
- void
2,类的加载与ClassLoader的理解
1,类的加载
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过 如下三个步骤来对该类进行初始化。
类加载器的作用:
-
**类加载的作用:**将class文件字节码内容加载到内存中,并将这些静态数据转换成方 法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为 方法区中类数据的访问入口。
-
**类缓存:**标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器 中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
2,ClassLoader
- 类加载器作用是用来把类(class)装载进内存的。JVM 规范定义了如下类型的 类的加载器。
-
几种类加载器操作
//1.获取一个系统类加载器 ClassLoader classloader = ClassLoader.getSystemClassLoader(); System.out.println(classloader); //2.获取系统类加载器的父类加载器,即扩展类加载器 classloader = classloader.getParent(); System.out.println(classloader); //3.获取扩展类加载器的父类加载器,即引导类加载器 classloader = classloader.getParent(); System.out.println(classloader); //4.测试当前类由哪个类加载器进行加载 classloader = Class.forName("exer2.ClassloaderDemo").getClassLoader(); System.out.println(classloader); //*6.关于类加载器的一个主要方法:getResourceAsStream(String str):获取类路 径下的指定文件的输入流 InputStream in = null; in = this.getClass().getClassLoader().getResourceAsStream("exer2\test.properties"); System.out.println(in);