Java反射机制
2021/10/23 14:11:28
本文主要是介绍Java反射机制,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
1.1反射机制的作用
通过java 语言中的发射机制可以操作字节码文件。
通过反射机制可以操作代码片段。(.class文件)。
1.2 反射机制的原理:
1、首先明确的概念: 一切皆对象----类也是对象.
2、然后知道类中的内容 :modifier(权限修饰符) constructor(构造方法) field(属性) method(方法).
3、其次明白加载: 当Animal.class在硬盘中时,是一个文件,当载入到内存中,可以认为是一个对象,是java.lang.class的对象.
1.3反射机制相关类所在包
java.lang.reflect.*;
1.4反射机制相关的重要类
java.lang.Class 代表整个字节码,代表一个类型(整个类)
java.lang.reflect.Method 代表字节码中的方法字节码(类中的方法)
java.lang.reflect.Constructor 代表字节码中的构造方法字节码(类中的构造方法)
java.lang.reflect.Field 代表字节码中的属性字节码(类中的成员变量:静态变量+实例变量)
1.5获取整个类的字节码的三种方式:
第一种:Class c = Class.forName("完整包名+类名");
第二种:Class c = 对象.getClass();
第三种:Class c=任何类型.class;
public static void main(String[] args) { Class c1=null; Class c2=null; try { /* 方法一:Class.forName():Class类下的forName()方法 1.静态方法 2.方法的参数是一个字符串,字符串需要是一个完整的类名 3.完整的类名必须带有包名。java.lang包也不可以省略 4.返回Class对象 */ //获取整个类的字节码文件 c1=Class.forName("java.lang.String");//c1代表String.class,或者说明c1代表String类型 c2=Class.forName("java.util.Date");//c2代表Date类型 Class c3=Class.forName("java.lang.Integer");//c3代表Integer类型 System.out.println(c1); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 方法二:java中任何一个对象(Object)都有一个方法:getClass() String str="www"; Class x=str.getClass();//x代表String.class字节码文件,x代表String类型 System.out.println(c1==x);//true(判断的是对象的内存地址) Date d=new Date(); Class dCl=d.getClass(); System.out.println(c2==dCl);//true(c2和dCl两个变量中保存的内存地址都是一样的,都指向这个方法区中的字节码文件) // 方法三:java语言中任何一种类型,包括基本数据类型,它都有.class属性 Class a = String.class;//a代表String类型 Class b = int.class;//b代表int类型 Class c = double.class;//c代表double类型 Class f = Date.class;//f代表Date类型 System.out.println(x==a);//true }
注意:字节码文件装载到JVM中时,只装载一份。
1.6 反射机制的应用
1.通过反射机制获得的Class,可以调用无参数构造方法来实例化对象
通过Class的newInstance()方法来实例化对象。
// c代表日期Date类型 Class c = Class.forName("java.util.Date"); // 实例化一个Date日期类型的对象 Object obj = c.newInstance();
注意:newInstance()方法内部实际调用了无参构造,必须确保无参构造存在,否则会出现"实例化"异常。
public class ReflectTest02 { public static void main(String[] args) { // 使用构造方法创建对象 User user=new User(); System.out.println(user); // 使用反射机制的方式创建对象 try { // 通过反射机制,获得Class,通过Class来实例化对象 Class c = Class.forName("com.wanho.po.User");//c代表User类型 // newInstance()方法调用User类的无参构造,完成对象的创建。 // 注意:newInstance()方法调用的是无参构造,要确保无参构造的存在。 Object obj = c.newInstance(); System.out.println(obj);//com.wanho.po.User@6d06d69c } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
2. 验证反射机制的灵活性
java代码写一遍,再不改变java源代码的基础之上,可以做到不同对象实例化。符合OCP开闭原则:对扩展开放,对修改关闭。
public class ReflectTest03 { public static void main(String[] args) throws Exception { // 只能创建一个User类对象 //User user = new User(); // 通过修改配置文件,创建不同的实例化对象 // 通过IO流读取classinfo.properties配置文件 // 工程下的相对路径/review/classinfo.properties FileReader reader = new FileReader("classinfo.properties"); // 创建属性类对象Map Properties pro = new Properties();//key value均是String类型 // 加载配置文件 pro.load(reader); // 关闭流 reader.close(); // 通过key获取value String className = pro.getProperty("className"); System.out.println("完整包名+类名:"+className); //通过反射机制实例化对象 Class c = Class.forName(className); Object obj = c.newInstance(); System.out.println("通过反射实例化的对象:"+obj); } }
3. 如果只想让一个类的“静态代码块”执行的话,可以使用Class.forName("该类的完整类名称")
重点:如果你只是希望一个类的静态代码块执行,其他代码一律不执行,可以使用:Class.forName("完整类名"); 这个方法的执行会导致类加载,类加载时,静态代码块执行。
public class ReflectTest04 { public static void main(String[] args) { try { //Class.forName()这个方法的执行会导致:类加载 Class.forName("com.wanho.reflect.MyClass"); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public class MyClass { //静态代码块在类加载时执行,并且只执行一次 static { System.out.println("MyClass类的静态代码块执行了!!!!"); } }
4.文件路径问题的研究
以下是获取一个文件的绝对路径的通用方式。前提是:文件需要在类路径下(src下),这种方式才可以使用。
public class AboutPath { public static void main(String[] args) throws Exception{ //这种方式的路径缺点是:移植性差,默认当前路径是project的根 //代码离开eclipse,到了其他位置,路径不是project的根了,这时这个路径就无效了 //FileReader reader = new FileReader("classinfo.properties"); //下面是一种比较通用的路径,即使代码换了位置,这样的编写仍然是通用的 //注意:使用以下通用方式的前提是:这个文件必须在类路径下。 //类路径下:凡是在src下的都是类路径下。 //src是类的根路径 /* 类加载器的种类:启动类加载器、应用类加载器、扩展类加载器 Thread.currentThread() 当前线程对象 getContextClassLoader() 是线程对象的方法,可以获取到当前线程的类加载器对象 getResource() 【获取资源】这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源 */ //以下获取文件的绝对路径是通用的 String path = Thread.currentThread().getContextClassLoader().getResource("classinfo2.properties").getPath(); //采用以上代码可以获得一个文件的绝对路径。 // /D:/software/eclipse-workspace/review/bin/classinfo2.properties System.out.println(path); // 获取db.properties文件的路径,需要从类的根路径下作为起点开始 String path2 = Thread.currentThread().getContextClassLoader().getResource("com/wanho/po/db.properties").getPath(); System.out.println(path2); } }
5.IoProperties.java
注意:classinfo2.properties配置文件在类路径(src)下
public class IoPropertiesTest { public static void main(String[] args) throws Exception{ //获取一个文件的绝对路径-----根据相对路径获取绝对路径 // String path = Thread.currentThread().getContextClassLoader().getResource("classinfo2.properties").getPath(); //System.out.println(path); // 读取配置文件 // FileReader reader = new FileReader(path); InputStream reader = Thread.currentThread().getContextClassLoader().getResourceAsStream("classinfo2.properties"); Properties pro = new Properties(); // 加载配置文件 pro.load(reader); reader.close(); // 根据key获取value String className = pro.getProperty("className"); System.out.println(className); } }
6. 资源绑定器
java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容。
注意:使用以下这种方式的时候,属性配置文件xxx.properties必须放到类路径下。
public class ResourceBundleTest { public static void main(String[] args) { // 资源绑定器,只能绑定xxx.properties文件。并且这个文件必须在类路径下。文件的扩展名也必须是properties // 注意:在写路径的时候,路径后面的扩展名不能写。 // ResourceBundle bundle = ResourceBundle.getBundle("classinfo2"); ResourceBundle bundle = ResourceBundle.getBundle("com/wanho/po/db"); // 根据key获得value String className = bundle.getString("className"); System.out.println(className); } }
1.7 反射属性Field(Field类)
1.7.1 获取Field对象
方法 | 含义 |
---|---|
getField(String name) | 获取指定的public修饰的属性对象 |
getFields() | 获取类中所有的使用public修饰的属性对象,返回属性数组Field[] |
getDeclaredField(String name) | 获取指定的属性对象 |
getDeclaredFields() | 获取所有属性对象,返回属性数组Field[] |
getModifiers() | 获取属性的修饰符列表,返回的修饰符是一个int型数字,每个数字是修饰符的代号 |
Modifier.toString(int i) | 将修饰符代号转换成字符串,返回String类型 |
getType() | 获取属性的类型,返回Class对象 |
public class ReflectTest05 { public static void main(String[] args) throws Exception{ // 获取整个类 Class studentClass = Class.forName("com.wanho.po.Student"); System.out.println("studentClass:"+studentClass); // 获取类名 String className = studentClass.getName(); System.out.println("完整类名:"+className); String simpleName = studentClass.getSimpleName(); System.out.println("简单类名:"+simpleName); System.out.println("================"); // 获取类中所有的使用public修饰的属性 Field[] fields = studentClass.getFields(); System.out.println(fields.length); // 取出属性 Field f = fields[0]; // 取出属性的名称 String fieldName = f.getName(); System.out.println(fieldName); // 获取所有属性 Field[] fields2 = studentClass.getDeclaredFields(); System.out.println(fields2.length); System.out.println("================"); // 遍历 for(Field field:fields2) { // private java.lang.String com.wanho.po.Student.name System.out.println(field); // 获取属性的修饰符列表 int i = field.getModifiers(); //返回的修饰符是一个数字,每个数字是修饰符的代号! System.out.println(i); // 将修饰符代号转换成字符串 String modifierString = Modifier.toString(i); System.out.println("属性的修饰符:"+modifierString); // 获取属性的类型 Class fieldType = field.getType(); //String fName = fieldType.getName(); //java.lang.String String fName = fieldType.getSimpleName(); //String System.out.println("属性的类型:"+fName); // 获取属性的名字 System.out.println("属性的名字:"+field.getName()); } } }
1.7.2 反编译Field
public class ReflectTest06 { public static void main(String[] args) throws Exception{ // 创建这个为了拼接字符串 StringBuilder sBuilder = new StringBuilder(); // 获取整个类 Class studentClass = Class.forName("com.wanho.po.Student"); sBuilder.append(Modifier.toString(studentClass.getModifiers())+" class "+studentClass.getSimpleName()+"{\n"); // 获取所有属性 Field[] fields = studentClass.getDeclaredFields(); for(Field field:fields) { sBuilder.append("\t"); sBuilder.append(Modifier.toString(field.getModifiers())); sBuilder.append(" "); sBuilder.append(field.getType().getSimpleName()); sBuilder.append(" "); sBuilder.append(field.getName()); sBuilder.append(";\n"); } sBuilder.append("}"); System.out.println(sBuilder); } }
1.7.3 通过反射机制访问对象属性(重点)
/** * (掌握):通过反射机制访问对象属性 * 怎么通过反射机制访问一个java对象的属性? * 给属性赋值set * 获取属性的值get */ public class ReflectTest07 { public static void main(String[] args) throws Exception{ //不使用反射机制,访问一个对象的属性 Student student = new Student(); student.no=1001;//给student对象的no属性赋值1001 //要素1:Student对象;要素2:no属性;要素3:属性值1001 // 读属性值 System.out.println(student.no); //使用反射机制,怎么去访问一个对象的属性。(set get) // 获取整个类 Class studentClass = Class.forName("com.wanho.po.Student"); Object obj = studentClass.newInstance();// Student对象。(底层调用无参构造方法) // 获取no属性(根据属性名获得属性) Field noField=studentClass.getDeclaredField("no"); // 给属性赋值 /* 虽然使用了反射机制,但是三要素还是缺一不可: 要素1:obj对象 要素2:no属性 要素3:no属性值1002 */ noField.set(obj, 1002);//给obj对象(Student对象)的no属性赋值1002 // 读取属性的值 // 两个要素:获取obj对象的no属性的值。 System.out.println(noField.get(obj)); // 可以访问私有的属性吗?---- 不可以 Field nameField=studentClass.getDeclaredField("name"); //System.out.println(nameField); // 打破封装(反射机制的缺点:打破封装,可能会给不法分子留下机会!!!) // 这样设置完之后,在外部也是可以访问private的。 nameField.setAccessible(true); // 给name属性赋值 nameField.set(obj, "marry"); // 获取name属性的值 System.out.println(nameField.get(obj)); } }
1.8 反射Method类(重点)
1.8.1 反射Method
方法 | 含义 |
---|---|
getMethods() | 获取所有公共public的方法对象 |
getMethod(String name, Class... parameterTypes) | 获取指定的公共public的方法对象 |
getDeclaredMethods() | 获取所有Method方法对象,返回Method[] |
getDeclaredMethod(String name, Class... parameterTypes) | 获取指定的方法对象 |
getParameterTypes() | 获取参数列表,返回Class[] |
public class ReflectTest08 { public static void main(String[] args) throws Exception{ // 获取类 Class uSClass = Class.forName("com.wanho.service.UserService"); // 获取所有方法Method Method[] methods = uSClass.getDeclaredMethods(); // 遍历 for(Method method:methods) { // 获取修饰符列表 System.out.println(Modifier.toString(method.getModifiers())); // 获取方法返回值类型 System.out.println(method.getReturnType().getSimpleName()); // 获取方法名 System.out.println(method.getName()); // 获取参数列表 Class[] parameterTypes = method.getParameterTypes(); // 遍历 for(Class parameterType:parameterTypes) { System.out.println(parameterType.getSimpleName()); } } } }
1.8.2 反编译Method
public class ReflectTest09 { public static void main(String[] args) throws Exception{ StringBuilder s = new StringBuilder(); // 获取类 Class uSClass = Class.forName("java.lang.String"); s.append(Modifier.toString(uSClass.getModifiers())+" class "+uSClass.getSimpleName()+"{\n"); Method[] methods = uSClass.getDeclaredMethods(); for(Method method:methods) { s.append("\t"); s.append(Modifier.toString(method.getModifiers())); s.append(" "); s.append(method.getReturnType().getSimpleName()); s.append(" "); s.append(method.getName()); s.append("("); Class[] parameterTypes = method.getParameterTypes(); // 遍历 for(Class parameterType:parameterTypes) { s.append(parameterType.getSimpleName()); s.append(","); } s.deleteCharAt(s.length()-1); s.append(")"); s.append("{}\n"); } s.append("}"); System.out.println(s); } }
1.8.3 通过反射机制调用方法(重点)
Method类中的invoke()方法
方法 | 含义 |
---|---|
invoke(Object obj, Object ...value) | 调用该方法 |
/** * (重点)通过反射机制怎么调用一个对象的方法 * 五个星※※※※※ * * 反射机制,让代码很具有通用性,可变化的内容都是写到配置文件当中, * 通过修改配置文件,可以创建不同的对象,调用不同的方法, * 但是java代码不需要做任何改动。这就是反射机制的魅力。 * @author Lucky * */ public class ReflectTest10 { public static void main(String[] args) throws Exception{ // 不使用反射机制,怎么调用方法 //创建对象 UserService userService = new UserService(); // 调用方法 /* 要素分析: 要素1:userService对象 要素2:login方法名 要素3:实参列表 要素4:返回值 */ boolean f= userService.login("admin", "123"); System.out.println(f); //userService.logout(); // 使用反射机制 Class c = Class.forName("com.wanho.service.UserService"); // 创建对象 Object obj = c.newInstance(); // 获取方法对象 Method loginMethod = c.getDeclaredMethod("login", String.class,String.class); // 调用方法 // 调用方法需要4个要素。 // 反射机制中最最最重要的一个方法,必须记住。 // 四要素 /* loginMethod方法 obj对象 "admin"、"123"实际参数 retValue返回值 */ Object retValue = loginMethod.invoke(obj, "admin", "123");//使用invoke调用方法 System.out.println(retValue); } }
1.9 反射Constructor
1.9.1 反编译Constructor
public class ReflectTest11 { public static void main(String[] args) throws Exception{ StringBuilder s = new StringBuilder(); Class vipClass = Class.forName("com.wanho.po.Vip"); // 获取类的修饰符 s.append(Modifier.toString(vipClass.getModifiers())); s.append(" class "); // 获取类名 s.append(vipClass.getSimpleName()); s.append("{\n"); // 拼接构造方法 Constructor[] constructors = vipClass.getDeclaredConstructors(); for(Constructor constructor : constructors) { s.append("\t"); // 获取构造方法的修饰符 s.append(Modifier.toString(constructor.getModifiers())); s.append(" "); s.append(vipClass.getSimpleName()); s.append("("); // 拼接参数 Class[] parameterTypes = constructor.getParameterTypes(); for(Class parameterType : parameterTypes) { s.append(parameterType.getSimpleName()); s.append(","); } if(parameterTypes.length > 0) { // 删除最后下标位置上的字符串 s.deleteCharAt(s.length() - 1); } s.append("){}\n"); } s.append("}"); System.out.println(s); } }
1.9.2 通过反射机制调用构造方法
public class ReflectTest12 { public static void main(String[] args) throws Exception{ // 不使用反射机制创建对象 Vip vip = new Vip(); Vip vip2 = new Vip(1001, "marry", "1999", true); // 使用反射机制创建对象 Class vipClass = Class.forName("com.wanho.po.Vip"); // 调用无参构造 Object obj = vipClass.newInstance(); System.out.println(obj); // 调用有参构造方法 // 1.获取构造方法 Constructor con = vipClass.getDeclaredConstructor(int.class); // 2.调用构造方法的new对象 Object obj2 = con.newInstance(1002); System.out.println(obj2); // 获取无参构造的方法 Constructor con2 = vipClass.getDeclaredConstructor(); Object obj3 = con2.newInstance(); System.out.println(obj3); } }
1.10 通过反射获取父类和父接口
public class ReflectTest13 { public static void main(String[] args) throws Exception{ // 获取String类 Class stringClass = Class.forName("java.lang.String"); // 获取String类的父类 Class superClass = stringClass.getSuperclass(); System.out.println("String的父类:"+superClass.getName()); // 获取String类实现的所有接口(一个类可以实现多个接口) Class[] interfaces = stringClass.getInterfaces(); for(Class inter : interfaces) { System.out.println("String类实现的接口:"+inter.getName()); } } }
这篇关于Java反射机制的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2025-01-11cursor试用出现:Too many free trial accounts used on this machine 的解决方法
- 2025-01-11百万架构师第十四课:源码分析:Spring 源码分析:深入分析IOC那些鲜为人知的细节|JavaGuide
- 2025-01-11不得不了解的高效AI办公工具API
- 2025-01-102025 蛇年,J 人直播带货内容审核团队必备的办公软件有哪 6 款?
- 2025-01-10高效运营背后的支柱:文档管理优化指南
- 2025-01-10年末压力山大?试试优化你的文档管理
- 2025-01-10跨部门协作中的进度追踪重要性解析
- 2025-01-10总结 JavaScript 中的变体函数调用方式
- 2025-01-10HR团队如何通过数据驱动提升管理效率?6个策略
- 2025-01-10WBS实战指南:如何一步步构建高效项目管理框架?