解决方案

Java 反射详解

seo靠我 2023-09-22 20:12:20

一、反射

1、什么是反射

反射允许对成员变量、成员方法和构造器的信息进行编程访问。

补充:暴力反射,非public修饰需要打开权限

setAccessible(boolean)

2、反射的作用

利用反射创建的对象SEO靠我可以无视修饰符调用类里面的内容可以跟配置文件结合起来使用

,把要创建的对象信息和方法写在配置文件中。

读取到什么类,就创建什么类的对象

读取到什么方法,就调用什么方法

此时当需求变更的时候不需要修改代码,只要SEO靠我修改配置文件即可。

简单记忆:1、获取任意一个类中的所有信息、2、结合配置文件动态创建对象。

3、学习反射到底学习什么

反射都是从class字节码文件中获取的内容。

如何获取class字节码文件的对象利用反射SEO靠我如何获取构造方法(创建对象)利用反射如何获取成员变量(赋值,获取值)利用反射如何获取成员方法(运行)

4、获取字节码文件对象的三种方式

Class这个类里面的静态方法forName(“全类名”)(最常用)SEO靠我通过class属性获取 (一般更多的是当做参数进行传递)通过对象获取字节码文件对象 (当我们已经有了这个类的对象时,才可以使用)

代码演示:

package com.liming.myreflect;puSEO靠我blic class ReflectDemo01 {public static void main(String[] args) throws ClassNotFoundException {/** SEO靠我获取class对象的三种方式* 1、Class.forName("全类名");* 2、类名.class* 3、对象.getClass();* *///1、方式一//全类名:包名+类名//最为常用的ClSEO靠我ass clazz1 = Class.forName("com.liming.myreflect.Student01");//2、方式二//一般更多的是当做参数进行传递Class clazz2 = SSEO靠我tudent01.class;//3、方式三//当我们已经有了这个类的对象时,才可以使用Student01 stu = new Student01();Class clazz3 = stu.getClSEO靠我ass();System.out.println(clazz1 == clazz2);System.out.println(clazz2 == clazz3);} } SEO靠我 package com.liming.myreflect;public class Student01 {private String name;private int age;public StrSEO靠我ing gender;public Student01() {}public Student01(String name, int age, String gender) {this.name = nSEO靠我ame;this.age = age;this.gender = gender;}/*** 获取* @return name*/public String getName() {return nameSEO靠我;}/*** 设置* @param name*/public void setName(String name) {this.name = name;}/*** 获取* @return age*/puSEO靠我blic int getAge() {return age;}/*** 设置* @param age*/public void setAge(int age) {this.age = age;}pubSEO靠我lic String toString() {return "Student01{name = " + name + ", age = " + age + "}";}/*** 获取* @return SEO靠我gender*/public String getGender() {return gender;}/*** 设置* @param gender*/public void setGender(StriSEO靠我ng gender) {this.gender = gender;} }

5、获取构造方法

规则:

​ 1、get表示获取

​ 2、Declared表示私有

​ 3、最后的s表示所有,复数形式

SEO靠我 4、如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用

代码演示:

public class ReflectDemo2 {public static void main(String[] aSEO靠我rgs) throws ClassNotFoundException, NoSuchMethodException {//1.获得整体(class字节码文件对象)Class clazz = ClassSEO靠我.forName("com.liming.myreflect.Student01");//2.获取构造方法对象//获取所有构造方法(public)Constructor[] constructors1SEO靠我 = clazz.getConstructors();for (Constructor constructor : constructors1) {System.out.println(construSEO靠我ctor);}System.out.println("=======================");//获取所有构造(带私有的)Constructor[] constructors2 = claSEO靠我zz.getDeclaredConstructors();for (Constructor constructor : constructors2) {System.out.println(constSEO靠我ructor);}System.out.println("=======================");//获取指定的空参构造Constructor con1 = clazz.getConstrSEO靠我uctor();System.out.println(con1);Constructor con2 = clazz.getConstructor(String.class,int.class);SysSEO靠我tem.out.println(con2);System.out.println("=======================");//获取指定的构造(所有构造都可以获取到,包括public包括pSEO靠我rivate)Constructor con3 = clazz.getDeclaredConstructor();System.out.println(con3);//了解 System.out.prSEO靠我intln(con3 == con1);//每一次获取构造方法对象的时候,都会新new一个。Constructor con4 = clazz.getDeclaredConstructor(StringSEO靠我.class);System.out.println(con4);} } 5.1、获取构造方法并创建对象

涉及到的方法:newInstance

代码演示:

//首先要有一个SEO靠我javabean类 public class Student {private String name;private int age;public Student() {}publiSEO靠我c Student(String name) {this.name = name;}private Student(String name, int age) {this.name = name;thSEO靠我is.age = age;}/*** 获取* @return name*/public String getName() {return name;}/*** 设置* @param name*/pubSEO靠我lic void setName(String name) {this.name = name;}/*** 获取* @return age*/public int getAge() {return aSEO靠我ge;}/*** 设置* @param age*/public void setAge(int age) {this.age = age;}public String toString() {retuSEO靠我rn "Student{name = " + name + ", age = " + age + "}";} }//测试类中的代码: //需求1: //SEO靠我获取空参,并创建对象//1.获取整体的字节码文件对象 Class clazz = Class.forName("com.liming.myreflect.Student"); SEO靠我 //2.获取空参的构造方法 Constructor con = clazz.getConstructor(); //3.利用空参构造方法创建对象 SEO靠我 Student stu = (Student) con.newInstance(); System.out.println(stu);System.out.println("====SEO靠我=========================================");//测试类中的代码: //需求2: //获取带参构造,并创建对象 SEO靠我 //1.获取整体的字节码文件对象 Class clazz = Class.forName("com.liming.myreflect.Student"); //2.获SEO靠我取有参构造方法 Constructor con = clazz.getDeclaredConstructor(String.class, int.class); //3SEO靠我.临时修改构造方法的访问权限(暴力反射) con.setAccessible(true); //4.直接创建对象 Student stu = (StudSEO靠我ent) con.newInstance("zhangsan", 23); System.out.println(stu);

6、获取成员变量

规则:

​ 1、get表示获取

​ 2、DeclSEO靠我ared表示私有

​ 3、最后的s表示所有,复数形式

​ 4、如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用

获取成员变量并获取值和修改值

方法说明void set(Object obj, OSEO靠我bject value)赋值Object get(Object obj)获取值 package com.liming.myreflect;import java.lang.reflecSEO靠我t.Field;public class ReflectDemo03 {public static void main(String[] args) throws Exception {//1、获取类SEO靠我的字节码文件Class<?> clazz = Class.forName("com.liming.myreflect.Student01");//2、获取所有公共的成员变量Field[] fieldsSEO靠我1 = clazz.getFields();for (Field field : fields1) {System.out.println(field);}//获取所有的成员变量Field[] fieSEO靠我lds2 = clazz.getDeclaredFields();for (Field field : fields2) {System.out.println(field);}//获取单个公共成员变SEO靠我量Field gender = clazz.getField("gender");System.out.println(gender);//获取单个成员变量(所有权限)Field name = claSEO靠我zz.getDeclaredField("name");System.out.println(name);//获取权限修饰符int modifiers = name.getModifiers();SySEO靠我stem.out.println(modifiers);//获取成员变量的名字String n = name.getName();System.out.println(n);//获取成员变量的数据类型SEO靠我Class<?> type = name.getType();System.out.println(type);//获取成员变量记录的值Student01 stu = new Student01("zSEO靠我hangsan", 23, "男");name.setAccessible(true);String value = (String) name.get(stu);System.out.printlnSEO靠我(value);//修改对象里面记录的值name.set(stu, "lisi");System.out.println(stu);} }

7、获取成员方法

规则:

​ 1、get表示获取

SEO靠我 2、Declared表示私有

​ 3、最后的s表示所有,复数形式

​ 4、如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用

获取成员方法并运行

Object invoke(Object objSEO靠我, Object… args) :运行方法

参数一:用obj对象调用该方法

参数二:调用方法的传递的参数(如果没有就不写)

返回值:方法的返回值(如果没有就不写)

代码演示:

package com.liminSEO靠我g.myreflect;import java.lang.reflect.Method; import java.lang.reflect.Parameter;public classSEO靠我 ReflectDemo03 {public static void main(String[] args) throws Exception {//1、获取class字节码文件对象Class claSEO靠我zz = Class.forName("com.liming.myreflect.Student02");//2、获取所有公共的成员方法(包含父类中所有的公共方法)Method[] methods =SEO靠我 clazz.getMethods();for (Method method : methods) {System.out.println(method);}System.out.println("=SEO靠我=====================================");//获取所有的成员方法(不包含父类)Method[] declaredMethods = clazz.getDeclarSEO靠我edMethods();for (Method method : declaredMethods) {System.out.println(method);}System.out.println("=SEO靠我=====================================");//获取单个公共的成员方法Method sleep = clazz.getMethod("sleep");System.SEO靠我out.println(sleep);System.out.println("======================================");//获取单个成员方法Method eatSEO靠我 = clazz.getDeclaredMethod("eat", String.class);System.out.println(eat);System.out.println("========SEO靠我==============================");//获取方法的修饰符int modifiers = eat.getModifiers();System.out.println(modSEO靠我ifiers);//获取方法的名字String name = eat.getName();System.out.println(name);//获取方法的形参Parameter[] parameterSEO靠我s = eat.getParameters();for (Parameter parameter : parameters) {System.out.println(parameter);}//获取方SEO靠我法抛出的异常Class[] exceptionTypes = eat.getExceptionTypes();for (Class exceptionType : exceptionTypes) {SSEO靠我ystem.out.println(exceptionType);}//方法运行//参数一:用obj对象调用该方法//参数二:调用方法传递的参数(没有就不写)//返回值:方法的返回值(没有就不写)StSEO靠我udent02 stu = new Student02();eat.setAccessible(true);eat.invoke(stu,"鸡腿");} } packSEO靠我age com.liming.myreflect;public class Student02 {private String name;private int age;public Student0SEO靠我2() {}public Student02(String name, int age) {this.name = name;this.age = age;}/*** 获取* @return nameSEO靠我*/public String getName() {return name;}/*** 设置* @param name*/public void setName(String name) {thisSEO靠我.name = name;}/*** 获取* @return age*/public int getAge() {return age;}/*** 设置* @param age*/public voiSEO靠我d setAge(int age) {this.age = age;}public void sleep(){System.out.println("睡觉");}private void eat(StSEO靠我ring something){System.out.println("在吃"+something);}public String toString() {return "Student02{nameSEO靠我 = " + name + ", age = " + age + "}";} }

8、四道面试题

你觉得反射好不好?好,有两个方向

第一个方向:无视修饰符访问类中的内容。但是这种操作在开发中SEO靠我一般不用,都是框架底层来用的。

第二个方向:反射可以跟配置文件结合起来使用,动态的创建对象,动态的调用方法。

​ 8.1、泛型擦除

集合中的泛型只在java文件中存在,当编译成class文件之后,就没有泛型了SEO靠我

代码演示:

public class ReflectDemo03 {public static void main(String[] args) throws NoSuchMethodExceptioSEO靠我n, InvocationTargetException, IllegalAccessException {//1.创建集合对象ArrayList<Integer> list = new ArrayLSEO靠我ist<>();list.add(123);//list.add("asd");//2.利用反射运行add方法去添加字符串//因为反射使用的是class字节码文件//获取class对象Class clSEO靠我azz = list.getClass();//获取add方法对象Method add = clazz.getMethod("add", Object.class);//运行方法add.invoke(SEO靠我list,"asd");//打印集合System.out.println(list);} } 8.2、修改字符串的内容

字符串不可以修改的原因?字符串,在底层是一个bySEO靠我te类型的字节数组,名字叫做value

private final byte[] value;

真正不能被修改的原因:final和private

final修饰value表示value记录的地址值不能修改。SEO靠我

private修饰value而且没有对外提供getvalue和setvalue的方法。所以,在外界不能获取或修改value记录的地址值。

如果要强行修改可以用反射:

代码演示:

public class RSEO靠我eflectDemo04 {public static void main(String[] args) throws IllegalAccessException, NoSuchFieldExcepSEO靠我tion {String s = "abc";String ss = "abc";// private final byte[] value= {97,98,99};// 没有对外提供getvalueSEO靠我和setvalue的方法,不能修改value记录的地址值// 如果我们利用反射获取了value的地址值,也是可以修改的// final修饰的value真正不可变的value数组的地址值,里面的内容利用SEO靠我反射还是可以修改的,比较危险//1.获取class对象Class clazz = s.getClass();//2.获取value成员变量(private)Field field = clazz.geSEO靠我tDeclaredField("value");//JDK高版本已经屏蔽了这种操作,低版本还是可以的field.setAccessible(true);//临时修改权限//3.获取value记录的地址SEO靠我值byte[] value = (byte[]) field.get(s);value[0] = 100;System.out.println(s);System.out.println(ss);} SEO靠我 } 8.3、反射和配置文件结合动态获取的练习(重点)

需求: 利用反射根据文件中的不同类名和方法名,创建不同的对象并调用方法。

分析:

①通过Properties加载配置文SEO靠我

②得到类名和方法名

③通过类名反射得到Class对象

④通过Class对象创建一个对象

⑤通过Class对象得到方法

⑥调用方法

代码演示: properties配置文件:

classname=com.liminSEO靠我g.mytest.Student methodname=sleep public class ReflectDemo01 {public static void maSEO靠我in(String[] args) throws Exception{//1.读取配置文件的信息Properties prop = new Properties();FileInputStream fSEO靠我is = new FileInputStream("day15\\prop.properties");prop.load(fis);fis.close();System.out.println(proSEO靠我p);String classname = prop.get("classname") + "";String methodname = prop.get("methodname") + "";//2SEO靠我.获取字节码文件对象Class clazz = Class.forName(classname);//3.获取构造器创建这个类的对象Constructor constructor = clazz.geSEO靠我tDeclaredConstructor();constructor.setAccessible(true);Object o = constructor.newInstance();System.oSEO靠我ut.println(o);//4.获取方法的对象Method sleep = clazz.getDeclaredMethod(methodname);sleep.setAccessible(trueSEO靠我);//5.运行方法sleep.invoke(o);} } 8.4、利用发射保存对象中的信息(重点)

需求:对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中SEO靠我

代码演示:

public class ReflectDemo02 {public static void main(String[] args) throws IOException, IllegalSEO靠我AccessException {/* 对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中去 */Student01 stu = new Student01("张三", 23, 男, 182SEO靠我.3, "学习");Teacher teacher = new Teacher("赖桑", 20000.0);saveObject(stu);saveObject(teacher);}public sSEO靠我tatic void saveObject(Object obj) throws IllegalAccessException, IOException {//1.获取字节码文件的对象Class clSEO靠我azz = obj.getClass();//2. 创建IO流BufferedWriter bw = new BufferedWriter(new FileWriter("day15" + File.SEO靠我separator + "a.txt", true));//3. 获取所有的成员变量Field[] fields = clazz.getDeclaredFields();for (Field fielSEO靠我d : fields) {field.setAccessible(true);//获取成员变量的名字String name = field.getName();//获取成员变量的值Object valSEO靠我ue = field.get(obj);//写出数据bw.write(name + "=" + value);bw.newLine();}bw.close();} }

运行结果: 在a.SEO靠我txt文件中

name=赖桑 salary=20000.0 name=张三 age=23 gender=男 height=SEO靠我182.3 hobby=学习 name=赖桑 salary=20000.0
“SEO靠我”的新闻页面文章、图片、音频、视频等稿件均为自媒体人、第三方机构发布或转载。如稿件涉及版权等问题,请与 我们联系删除或处理,客服邮箱:html5sh@163.com,稿件内容仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同 其观点或证实其内容的真实性。

网站备案号:浙ICP备17034767号-2