反射

来源:互联网 发布:再也没有 知乎 编辑:程序博客网 时间:2024/06/04 19:17


1.类的加载概述和加载时机

 1.1类的加载概述

* 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
* 加载 * 就是指将class文件读入内存,并为之创建一个Class对象。任何类被使用时系统都会建立一个Class对象。* 连接(3)* 验证 是否有正确的内部结构,并和其他类协调一致* 准备 负责为类的静态成员分配内存,并设置默认初始化值* 解析 将类的二进制数据中的符号引用替换为直接引用* 初始化 


 1.2加载时机(什么时候.class字节码文件会被加载进内存)

      * 创建类的实例* 访问类的静态变量,或者为静态变量赋值* 调用类的静态方法* 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象* 初始化某个类的子类* 直接使用java.exe命令来运行某个主类


2类加载器的概述和分类

 2.1类加载器的概述

      * 负责将.class文件加载到内存中,并为之生成对应的Class对象。* 虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。

 2.2类加载器的分类

      * Bootstrap ClassLoader 根类加载器* Extension ClassLoader 扩展类加载器* System ClassLoader 系统类加载器

 2.3类加载器的作用

* Bootstrap ClassLoader 根类加载器* 也被称为引导类加载器,负责Java核心类的加载* 比如System,String等。在JDK中JRE的lib目录下rt.jar文件中* Extension ClassLoader 扩展类加载器* 负责JRE的扩展目录中jar包的加载。* 在JDK中JRE的lib目录下ext目录* System ClassLoader 系统类加载器* 负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径


3反射概述

3.1 反射概述

* JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;* 对于任意一个对象,都能够调用它的任意一个方法和属性;* 这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。* 要想解剖一个类,必须先要获取到该类的字节码文件对象。* 而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象。


 3.2三种方式

     * a:Object类的getClass()方法,判断两个对象是否是同一个字节码文件* b:静态属性class,锁对象* c:Class类中静态方法forName(),读取配置文件


3.3案例演示

* 获取class文件对象的三种方式





public class a {/** * @param args * @throws ClassNotFoundException  */public static void main(String[] args) throws ClassNotFoundException {// TODO Auto-generated method stubClass clazz1 = Class.forName("bean.Person");Class clazz2 = Person.class;Person p = new Person();Class clazz3 = p.getClass();System.out.println(clazz1 == clazz2);//trueSystem.out.println(clazz2 == clazz3);//trueSystem.out.println(clazz1);//class bean.PersonSystem.out.println(clazz3);}}

Person对象类


package bean;public class Person {private int age;private String name;public Person(){super();}public Person(int age, String name) {//alt shift ssuper();this.age = age;this.name = name;}/** * @return the age */public int getAge() {return age;}/** * @param age the age to set */public void setAge(int age) {this.age = age;}/** * @return the name */public String getName() {return name;}/** * @param name the name to set */public void setName(String name) {this.name = name;}/* (non-Javadoc) * @see java.lang.Object#hashCode() */@Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + age;result = prime * result + ((name == null) ? 0 : name.hashCode());return result;}/* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (this.getClass() != obj.getClass())//判断调用对象和传入对象的字节码文件是否是同一个字节码文件return false;Person other = (Person) obj;if (age != other.age)return false;if (name == null) {if (other.name != null)return false;} else if (!name.equals(other.name))return false;return true;}/* (non-Javadoc) * @see java.lang.Object#toString() */@Overridepublic String toString() {return "Person [age=" + age + ", name=" + name + "]";}public void  eat() {System.out.println("I eat an apple");}public void  eat( int n) {System.out.println("I eat "+ n +" apples");}}



4.类 Class<T>

java.lang.Object——java.lang.Class<T>类型参数:T - 由此 Class 对象建模的类的类型例如,String.class 的类型是 Class<String>。如果将被建模的类未知,则使用 Class<?>。所有已实现的接口: Serializable, AnnotatedElement, GenericDeclaration, Type public final class Class<T> extends Object implements Serializable...Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。 Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。 

public static Class<?> forName(String className) throws ClassNotFoundException返回与带有给定字符串名的类或接口相关联的 Class 对象。参数:className - 所需类的完全限定名。 返回:具有指定名的类的 Class 对象。 

public ClassLoader getClassLoader()返回该类的类加载器。有些实现可能使用 null 来表示引导类加载器。如果该类由引导类加载器加载,则此方法在这类实现中将返回 null。 

public Class<?>[] getInterfaces()确定此对象所表示的类或接口实现的接口。 如果此对象表示一个类,则返回值是一个数组,它包含了表示该类所实现的所有接口的对象。数组中接口对象顺序与此对象所表示的类的声明的 implements 子句中接口名顺序一致。

public T newInstance() throws InstantiationException,IllegalAccessException创建此 Class 对象所表示的类的一个新实例。如同用一个带有一个空参数列表的 new 表达式实例化该类。如果该类尚未初始化,则初始化这个类。 返回:此对象所表示的类的一个新分配的实例。 

Class类的newInstance()方法是使用该类无参的构造函数创建对象, 如果一个类没有无参的构造函数,就不能这样创建了可以通过调用Class类的getConstructor

public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException...返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。parameterTypes 参数是 Class 对象的一个数组,这些 Class 对象按声明顺序标识构造方法的形参类型。要反映的构造方法是此 Class 对象所表示的类的公共构造方法,其形参类型与 parameterTypes 所指定的参数类型相匹配。 参数:parameterTypes - 参数数组 返回:与指定的 parameterTypes 相匹配的公共构造方法的 Constructor 对象 


public Field getField(String name) throws NoSuchFieldException, SecurityException 返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。 name 参数是一个 String,用于指定所需字段的简称。 


public Field getDeclaredField(String name)throws NoSuchFieldException, SecurityException返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。name 参数是一个 String,它指定所需字段的简称。注意,此方法不反映数组类的 length 字段。


public Method getMethod(String name, Class<?>... parameterTypes) throws...返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。name 参数是一个 String,用于指定所需方法的简称。parameterTypes 参数是按声明顺序标识该方法形参类型的 Class 对象的一个数组。如果 parameterTypes 为 null,则按空数组处理。 


5.反射获取构造方法


Constructor<T>

java.lang.Object——java.lang.reflect.AccessibleObject————java.lang.reflect.Constructor<T>类型参数:T - 在其中声明构造方法的类。所有已实现的接口: AnnotatedElement, GenericDeclaration, Member public final class Constructor<T> extends AccessibleObjectimplements ...提供关于类的单个构造方法的信息以及对它的访问权限。 Constructor 允许在将实参与带有底层构造方法的形参的 newInstance() 匹配时进行扩展转换,

public T newInstance(Object... initargs) throws ...使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。个别参数会自动解包,以匹配基本形参,必要时,基本参数和引用参数都要进行方法调用转换。 如果底层构造方法所需形参数为 0,则所提供的 initargs 数组的长度可能为 0 或 null。 如果构造方法正常完成,则返回新创建且已初始化的实例。 参数:initargs - 将作为变量传递给构造方法调用的对象数组; 返回:通过调用此对象表示的构造方法来创建的新对象 


案例:*通过反射获取无参构造方法并使用
* 反射(Class.forName()读取配置文件

import java.io.BufferedReader;import java.io.FileReader;import java.io.IOException;public class b {/** * 榨汁机(Juicer)榨汁的案例 * 分别有水果(Fruit)苹果(Apple)香蕉(Banana)桔子(Orange)榨汁(squeeze) * @throws IOException  */public static void main(String[] args) throws Exception {Juicer j = new Juicer();//创建榨汁机j.run(new Apple());j.run(new Orange());//重载优化为多态,ok//用反射和配置Class clazz1 = Class.forName("Apple");//默认包下System.out.println(clazz1);//class Apple/*向config.properties配置文件中第一行写 Apple 没有双引号 * 用BufferedReader可以读取整行*/BufferedReader br = new BufferedReader(new FileReader("config.properties"));Class clazz2 = Class.forName(br.readLine());//获取该类的字节码文件System.out.println(clazz1);Fruit f = (Fruit) clazz2.newInstance();//创建此class文件实例对象//父类引用指向了子类对象,水果的引用指向了apple对象j.run(f);//以后就直接在配置文件中修改,操作简单}}//2.向上抽取水果,用多态的方法做interface Fruit {public void squeeze();}class Apple implements Fruit {public void squeeze() {System.out.println("榨出一杯苹果汁儿");}}class Orange implements Fruit {public void squeeze() {System.out.println("榨出一杯橘子汁儿");}}//1.重载class Juicer {/*public void run(Apple a) {a.squeeze();}public void run(Orange o) {o.squeeze();}*///用多态改进的代码如下public void run(Fruit f) {f.squeeze();}}

案例:*通过反射获取有参构造方法并使用


import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;import bean.Person;public class c {/** * @param args * (int.class,String.class)方法获取一个指定的构造函数 * 然后再调用Constructor类的newInstance(23,"小鱼")方法创建对象 * @throws Exception  */public static void main(String[] args) throws Exception {Class clazz = Class.forName("bean.Person");Person p = (Person) clazz.newInstance();//通过无参构造创建对象System.out.println(p);//Person [age=0, name=null]/*但是如果注释掉person的无参构造,则会出现异常*/Constructor c = clazz.getConstructor(int.class,String.class);//获取有参构造.注意形参顺序Person p1 = (Person) c.newInstance(23,"小鱼");//通过有参构造创建对象System.out.println(p1);//Person [age=23, name=小鱼]}}


6.通过反射获取成员变量并使用


Field

java.lang.Object——java.lang.reflect.AccessibleObject————java.lang.reflect.Field所有已实现的接口: AnnotatedElement, Member public final class Field extends AccessibleObject implements MemberField 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。

public Object get(Object obj) throws IllegalArgumentException...返回指定对象上此 Field 表示的字段的值。如果该值是一个基本类型值,则自动将其包装在一个对象中。 

public void set(Object obj,Object value) throws IllegalArgumentException...将指定对象变量上此 Field 对象表示的字段设置为指定的新值。如果底层字段的类型为基本类型,则对新值进行自动解包。 

从java.lang.reflect.AccessibleObject 继承的方法
public void setAccessible(boolean flag) throws SecurityException将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。 


public class d {/**  * @throws Exception  */public static void main(String[] args) throws Exception {Class clazz = Class.forName("bean.Person");Constructor c = clazz.getConstructor(int.class,String.class);//获取有参构造Person p = (Person) c.newInstance(23,"张三");//通过有参构造创建对象//Field f = clazz.getField("name");//获取姓名字段//f.set(p, "李四");//修改姓名的值Field f = clazz.getDeclaredField("name");//暴力反射获取字段f.setAccessible(true);//去除私有权限f.set(p, "李四");System.out.println(p);//Person [age=23, name=李四]}}

Class.getField(String)方法可以获取类中的指定字段(可见的), * 如果是私有的可以用getDeclaedField("name")方法获取,通过set(obj, "李四")方法可以设置指定对象上该字段的值, * 如果是私有的需要先调用setAccessible(true)设置访问权限,用获取的指定的字段调用get(obj)可以获取指定对象中该字段的值


7.通过反射获取方法并使用


Method

java.lang.Object——java.lang.reflect.AccessibleObject————java.lang.reflect.Method所有已实现的接口: AnnotatedElement, GenericDeclaration, Member public final class Method extends AccessibleObject implements GenericDeclaration, MemberMethod 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。 

public Object invoke(Object obj, Object... args) throws..对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。个别参数被自动解包,以便与基本形参相匹配,基本参数和引用参数都随需服从方法调用转换。参数:obj - 从中调用底层方法的对象(必须的98)args - 用于方法调用的参数,如果为null则表示无参方法返回:使用参数 args 在 obj 上指派该对象所表示方法的结果 


案例:person类中加了一个eat()方法和一个重载eat(int)

 Class.getMethod(String, Class...) 和 Class.getDeclaredMethod(String,Class...)方法可以获取类中的指定方法,调用invoke(Object,Object...)可以调用该方法,* Class.getMethod("eat").invoke(obj)* Class.getMethod("eat",int.class).invoke(obj,10)


public class e {/** * @throws Exception  */public static void main(String[] args) throws Exception {Class clazz = Class.forName("bean.Person");Constructor c = clazz.getConstructor(int.class,String.class);//获取有参构造Person p = (Person) c.newInstance(22,"小鱼");//通过有参构造创建对象Method m = clazz.getMethod("eat");//获取eat方法m.invoke(p);Method m2 = clazz.getMethod("eat", int.class);//获取有参的eat方法m2.invoke(p, 10);}}


8.通过反射越过泛型检查


泛型檫除也叫泛型反射,泛型只在编译期有效,在运行期会被擦除掉 * ArrayList<Integer>的一个对象,在这个集合中添加一个字符串数据,如何实现呢?

public class f {/** * @param args * @throws Exception  */public static void main(String[] args) throws Exception {// TODO Auto-generated method stubArrayList<Integer> list = new ArrayList<>();list.add(111);list.add(222);Class clazz = Class.forName("java.util.ArrayList");//获取字节码对象Method m = clazz.getMethod("add", Object.class);//获取add方法m.invoke(list, "abc");System.out.println(list);}}


9.小练习

9.1

已知一个类,定义如下(在默认包下)

public class Demo {public void run() {System.out.println("welcome");}}

1.写一个Properties格式的配置文件,配置类的完整名称。 
2.写一个程序,读取这个Properties配置文件,获得此类的完整名称并加载这个类,用反射的方式运行run方法。

public class g {/* * @throws Exception  */public static void main(String[] args) throws Exception {BufferedReader br = new BufferedReader(new FileReader("config.properties"));//创建输入流关联config.propertiesClass clazz = Class.forName(br.readLine());//读取配置文件中类名,获取字节码对象Demo d = (Demo) clazz.newInstance();//通过字节码对象创建对象d.run();}}

9.2

写一个方法
public void setProperty(Object obj, String propertyName, Object value){}
此方法可将obj对象中名为propertyName的属性的值设置为value


这个工具类如下

package bean;import java.lang.reflect.Field;public class Tool {public void setProperty(Object obj, String propertyName, Object value) throws Exception {Class clazz = obj.getClass();//获取字节码对象Field f = clazz.getDeclaredField(propertyName);//暴力反射获取字段f.setAccessible(true);//去除权限f.set(obj, value);}}

测试

import bean.Person;import bean.Tool;public class test {/** * @param args * @throws Exception  */public static void main(String[] args) throws Exception {// TODO Auto-generated method stubPerson p = new Person(23, "张三");System.out.println(p);Tool t = new Tool();t.setProperty(p, "name", "小小");System.out.println(p);}}








原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 我的世界手机版狼变色怎么办 我的世界开了光影太阳太刺眼怎么办 我的世界饥饿值掉的慢怎么办 我的世界合装备过于昂贵怎么办 我的世界故事模式屏幕是黑的怎么办 人物只剩下轮廓的图用ps怎么办 两年义务兵考军校分数不够怎么办 大学生兵考上军校后原学籍怎么办 我的世界工业附魔到精准采集怎么办 交换生在台期间遗失通行证怎么办 驾驶证上的号码是士兵证号怎么办 士兵证丢了但是要买飞机票怎么办 君泰保安公司不发工资怎么办 冬天洗棉衣后有一圈白色怎么办 买了一批化肥没有执行标准怎么办 防护栏下面打不了膨胀螺丝怎么办 不知道怀孕照了x射线怎么办 腹部照了x光片照了三次怎么办 像在工厂戴的静电帽弄丢了怎么办 诈骗犯把钱被转到别人账户怎么办 狗狗5个月在家随地大小便怎么办 上课放屁放的快没憋到老是放怎么办 丈夫有外遇并跟小三有一儿子怎么办 借款夫妻双亡借出去的钱怎么办? 橡胶底的劳保鞋开胶了怎么办? 求部队停止有偿服务内部超市怎么办 晋江买了全本还是有防盗章节怎么办 宝宝没有穿衣服的地方长疙瘩怎么办 詹姆斯士兵12魔术贴老是掉怎么办 手机版本不支持陌陌视频聊天怎么办 私人单位不给员工写收入证明怎么办 cad图形缩小后找不到图了怎么办 离婚了老婆嫁给了别人怎么办 对方开车撞伤人逃逸不赔钱怎么办 帮老板开车撞伤人老板不愿赔怎么办 我开车撞人现在伤者住院怎么办 B照驾驶证扣3分怎么办l 驾驶证被盗后被别人拿去消分怎么办 碰瓷的手碰我后视镜怎么办 摩托被盗监控录像器没有记录怎么办 车贷逾期车被贷款公司拖走了怎么办