反射 约定不等于承诺〃 2022-06-15 01:47 140阅读 0赞 # 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文件对象的三种方式 ![Center][] public class a { /** * @param args * @throws ClassNotFoundException */ public static void main(String[] args) throws ClassNotFoundException { // TODO Auto-generated method stub Class clazz1 = Class.forName("bean.Person"); Class clazz2 = Person.class; Person p = new Person(); Class clazz3 = p.getClass(); System.out.println(clazz1 == clazz2);//true System.out.println(clazz2 == clazz3);//true System.out.println(clazz1);//class bean.Person System.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 s super(); 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() */ @Override public 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) */ @Override public 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() */ @Override public 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 stub ArrayList<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.properties Class 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 stub Person p = new Person(23, "张三"); System.out.println(p); Tool t = new Tool(); t.setProperty(p, "name", "小小"); System.out.println(p); } } [Center]: /images/20220615/0551831038c048e9bfb654aedf976fde.png
相关 Java反射-反射 API 转自:https://www.jianshu.com/p/e55770dd48d3 涉及这些类 ![watermark_type_ZmFuZ3poZW5naGVpdGk_s 深碍√TFBOYSˉ_/ 2023年01月17日 07:40/ 0 赞/ 145 阅读
相关 反射_反射概述 反射 JAVA反射机制是在运行状态中,对于任意一个 类,都能够知道这个类的所有属性和方法;对 于任意一个对象,都能够调用它的任意一个方 清疚/ 2022年06月17日 02:22/ 0 赞/ 201 阅读
相关 反射 1.类的加载概述和加载时机 1.1类的加载概述 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类 约定不等于承诺〃/ 2022年06月15日 01:47/ 0 赞/ 141 阅读
相关 反射 在反射中如果不使用强制获取私有的方法,无论是再获取成员变量还是方法的时候获取被public修饰的 反射首先获取构造,在获取构造之后才可以获取成员的方法 在java的反射机制 拼搏现实的明天。/ 2022年04月24日 10:00/ 0 赞/ 137 阅读
相关 反射 知识点: 1. 类对象概念 2. Class类的使用:创建类对象 3. 动态加载类 4. 获取方法信息 5. 获取成员变量、构造函数信息 6. 方法反射的基本操作 比眉伴天荒/ 2022年04月14日 04:14/ 0 赞/ 155 阅读
相关 反射 // 1.定义一个标准的JavaBean,名叫Person,包含属性name、age。 // 使用反射的方式创建一个实例、调用构造函数初始化name、age,使用反射方式调 Dear 丶/ 2022年04月03日 08:16/ 0 赞/ 174 阅读
相关 反射 反射 类加载器的概述 当程序要使用某个类时,如果该类还未被加载到内存中, 则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。 加载 就是指将c 怼烎@/ 2022年01月17日 12:13/ 0 赞/ 194 阅读
相关 【反射】 JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法 比眉伴天荒/ 2021年11月09日 20:40/ 0 赞/ 448 阅读
相关 反射 反射机制简述 静态编译:在编译时确定类型,绑定对象,即通过。 动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的 小咪咪/ 2021年09月27日 14:00/ 0 赞/ 336 阅读
相关 反射 目录 1 ClassLoader 类装载器 -------------------- 类装载器的工作机制 类装载器就是寻找字节码文件并且构造出类在JVM内部表 雨点打透心脏的1/2处/ 2021年09月27日 08:42/ 0 赞/ 267 阅读
还没有评论,来说两句吧...