概述
通过Java语言中的反射机制可以操作字节码文件,优点是类似于黑客,可以读和修改字节码文件。。Java反射相关的类在java.lang.reflect.*包下,具体来讲,包括:
①java.lang.Class;代表字节码文件
②java.lang.reflect.Method;方法字节码
③java.lang.reflect.Constructor;构造器字节码
④java.lang.reflect.Field属性字节码
package reflect;/*** 字节码文件中的xxx字节码*///Class文件代表整个字节码文件public class User {//Field字节码,即属性字节码int no;//Constructor字节码,即构造器字节码public User() {}//Method字节码,即方法字节码public int getNo() {return no;}public void setNo(int no) {this.no = no;}}
想要获得字节码文件中的属性、方法或者构造器,首先要获得字节码文件。
3种方式获得字节码文件
①Class.forName(“类名”);
Class c1 = Class.forName(“java.util.String”); //c1代表的是String.class
②对象.getClass();
③包括基本数据类型中在内的任何一种类型,都有.class属性:
Class c = String.class,c代表String类型
package reflect;/*** 想要操作一个类的字节码,首先要获得这个类的字节码,有三种方式获得这个类的字节码文件(java.lang.Class)*/public class RefectTest01 {public static void main(String[] args) throws ClassNotFoundException {//方式一:c1,c2,c3是对应类的整个.class文件Class c1 = Class.forName("java.lang.String");Class c2 = Class.forName("java.util.Date");Class c3 = Class.forName("java.lang.System");//方式二:java中任何一个对象都有getClass()方法,旨在获取该对象所在的类String s = "abc";Class x = s.getClass(); //x代表的是String.class字节码文件,x代表的是String类型System.out.println(c1 == x); //true//方式三:java语言中,任何一种类型,包括基本数据类型,都有.class属性Class str = String.class; //str代表String类型System.out.println(c1 == str); //true}}

获得Class,能做什么?
获取对象
package reflect.bean;public class User {public User(){System.out.print("无参构造方法执行了!"+"\t");}//若没有无参构造方法,对象.newInstance()会出现实例化异常}package reflect;/*** 获取到Class之后,能做什么?* 通过Class的newInstance方法来实例化对象。* 注意:newInstance方法内部实际上调用了无参构造器,所以必须保证无参构造器存在才行* 那么问题来了:这种方式创建对象和常规方法相比,优势在哪?*/public class ReflectTest02 {//通过反射机制获取Class,通过Class来实例化对象public static void main(String[] args) {//使用反射机制创建对象try {Class c1 = Class.forName("reflect.bean.User");//这个方法会调用User这个类的无参构造方法,完成对象的创建Object obj = c1.newInstance();System.out.println(obj); //无参构造方法执行了! reflect.bean.User@1b6d3586} catch (ClassNotFoundException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();}//不使用反射机制创建对象User user = new User();System.out.println(user); //reflect.User@4554617c}}
如何体现发射机制的优越性?
在IDEA中创建以上文件,布局如右图:
package reflect;import java.io.FileReader;import java.io.IOException;import java.util.Properties;/*** 验证反射机制的灵活性:* Java代码写一遍,在不改变代码的基础上,可以做到不同对象的实例化,这就体现了反射的灵活性* 符合OCP原则:对扩展开放,对修改关闭* 高级框架例如:SSM、SSH的底层都使用了反射机制*/public class ReflectTest03 {public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {//用IO流读取classinfo.properties文件FileReader fr = new FileReader("D:\\DataValue\\project\\MyData\\Data1\\src\\reflect\\classinfo.properties");//创建属性类对象Properties pro = new Properties();//加载pro.load(fr);//关闭流fr.close();//通过key获取valueString className = pro.getProperty("className");System.out.println(className); //reflect.User//通过反射机制实例化对象Class c2 = Class.forName(className);Object obj = c2.newInstance();System.out.println(obj); //reflect.User@1b6d3586//以上代码不改,改配置文件的className,改为:java.util.Date//输出为:// java.util.Date// Thu Oct 14 08:00:56 CST 2021}}
Class.forName()的作用
package reflect;/*研究一下,Class.forName()发生了什么?Class.forName()这个方法的执行会导致类加载,类加载时静态代码块会执行!*/public class ReflectTest04 {public static void main(String[] args) throws ClassNotFoundException {Class.forName("reflect.MyClass"); //静态代码块执行了!}}class MyClass{//静态代码块在类加载时执行,且只执行一次static {System.out.println("静态代码块执行了!");}}
获取类路径下文件的绝对路径
package reflect;/*研究一下文件路径的问题相对路径的缺点:可移植性差,换个编译工具,可能就找不到该文件了*/public class AboutPathTest {public static void main(String[] args) {//方式一//使用以下方式的前提是,这个文件必须在类路径(src,src是类的根路径)下--Tcggg//当前线程对象的类加载器的根路径下加载资源获取绝对路径//这种方式获取绝对路径是通用的,不管是在哪个盘,什么系统String path = Thread.currentThread().getContextClassLoader().getResource("reflect/classinfo.properties").getPath();// /D:/DataValue/project/MyData/Data1/out/production/Data1/reflect/classinfo.properties//起点类的根路径System.out.println(path);//方式二:以流的形式返回/* String path1 = Thread.currentThread().getContextClassLoader().getResource("").getPath();FileReader fileReader = new FileReader(path1);*///以上2行可以合并为以下这行,直接以流形式返回InputStream reader = Thread.currentThread().getContextClassLoader().getResourceAsStream("reflect/classinfo.properties");Properties pro = new Properties();pro.load(reader);reader.close();String className = pro.getProperty("className");System.out.println(className); //java.util.Date}}
获取属性配置文件的内容
类及其他文件布局如下图:
package reflect.bean;public class User {String name;public User(String name) {this.name = name;}}
package reflect;import java.util.ResourceBundle;/*java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容使用以下这种方式的时候,属性配置文件xxx.properties必须放到类路径下*/public class ResourceBundleTest {public static void main(String[] args) {//资源绑定器,只能绑定xxx.properties文件。并且这个文件必须在类路径下,文件扩展名也必须是properties//并且在写路径的时候,路径后面的扩展名不能写//总之三个要求:①src目录下 ②文件是属性配置文件 ③后缀名不能写ResourceBundle bundle = ResourceBundle.getBundle("classinfo");String className = bundle.getString("className");System.out.println(className); //reflect.bean.User}}
类加载器概述
类加载器是负责专门加载类的命令或是工具,jdk自带的有3种类加载器:
- 启动类加载器(父)- 扩展类加载器(母)- 应用类加载器
父和母被称为双亲委派机制。先父后母,即如果你写了String类,Sun公司自己也有String类,按这个机制来,优先加载Sun公司写的String类。这是为了类加载的安全而设置的双亲委派机制。
假设有这样一段代码:String s = “abc”;
在代码开始执行之前,会将所需的类全部加载到JVM中。那么是怎么进行加载的呢?首先是启动类加载器去加载,加载的对象是jdk目录下jdk/jre/lib/rt.jar。(注:rt.jar中的都是JDK最核心的类库。)
如果通过启动类加载器加载不到,会通过扩展类加载器去加载。注意:扩展类加载器专门加载jre/lib/ext.jar。
如果扩展类加载器也没加载到,就会通过应用类加载器加载,这里加载的是classpath中的类(class文件).
获取属性对象
package reflect;import java.lang.reflect.Field;import java.lang.reflect.Modifier;/*反射Student类中所有的Field*/public class ReflectTest05 {public static void main(String[] args) throws ClassNotFoundException {//获取整个类Class studentClass = Class.forName("reflect.bean.Student");String className = studentClass.getName();System.out.println(className); //reflect.bean.StudentString simpleName = studentClass.getSimpleName();System.out.println(simpleName); //Student//获取类中所有public 修饰的FieldField[] fields = studentClass.getFields();System.out.println(fields.length); //1//取出这个FieldField f = fields[0];//取出这个Field的名字String fieldName = f.getName();System.out.println(fieldName); //no//获取类中所有FieldField[] fs = studentClass.getDeclaredFields();System.out.println("------------------------------------");System.out.println(fs.length);for (Field field : fs) {//获取属性的修饰符列表//返回的修饰符是一个数字,每个数字是修饰符的代号int i = field.getModifiers();// System.out.println(i);//可以将这个代号数字转换为“字符串”吗?String modifierStr = Modifier.toString(i);System.out.print(modifierStr+" ");//获取属性的类型Class fieldType = field.getType();// String fieldTypeName = fieldType.getName();// System.out.println(fieldTypeName);String fieldTypeSimpleName = fieldType.getSimpleName();System.out.print(fieldTypeSimpleName+" ");//获取属性的名字System.out.println(field.getName());}}}/*整体内容输出如下:------------------------------------5private String nameprotected int ageboolean genderpublic int nopublic static final double MATH_PI*/
反编译之Student类
package reflect;import java.lang.reflect.Field;import java.lang.reflect.Modifier;/*反编译Student这个Bean类只需要提供全类名*/public class ReflectTest06 {public static void main(String[] args) throws Exception{Class studentClass = Class.forName("reflect.bean.Student");StringBuilder s = new StringBuilder();s.append(Modifier.toString(studentClass.getModifiers()) + " class " + studentClass.getSimpleName() + "{\n");Field[] fields = studentClass.getDeclaredFields();for (Field field : fields) {s.append("\t");s.append(Modifier.toString(field.getModifiers())+" "+field.getType().getSimpleName()+" "+field.getName()+";\n");}s.append("}");System.out.println(s);}}/*反编译Students类的输出结果:public class Student{private String name;protected int age;boolean gender;public int no;public static final double MATH_PI;}*/
反编译之Date类
package reflect;import java.lang.reflect.Field;import java.lang.reflect.Modifier;public class ReflectTest06 {public static void main(String[] args) throws Exception{Class studentClass = Class.forName("java.util.Date");StringBuilder s = new StringBuilder();s.append(Modifier.toString(studentClass.getModifiers()) + " class " + studentClass.getSimpleName() + "{\n");Field[] fields = studentClass.getDeclaredFields();for (Field field : fields) {s.append("\t");s.append(Modifier.toString(field.getModifiers())+" "+field.getType().getSimpleName()+" "+field.getName()+";\n");}s.append("}");System.out.println(s);}}/*反编译Students类的输出结果:public class Date{private static final BaseCalendar gcal;private static BaseCalendar jcal;private transient long fastTime;private transient Date cdate;private static int defaultCenturyStart;private static final long serialVersionUID;private static final String[] wtb;private static final int[] ttb;}*/
反编译之String类
package reflect;import java.lang.reflect.Field;import java.lang.reflect.Modifier;public class ReflectTest06 {public static void main(String[] args) throws Exception{Class studentClass = Class.forName("java.lang.String");StringBuilder s = new StringBuilder();s.append(Modifier.toString(studentClass.getModifiers()) + " class " + studentClass.getSimpleName() + "{\n");Field[] fields = studentClass.getDeclaredFields();for (Field field : fields) {s.append("\t");s.append(Modifier.toString(field.getModifiers())+" "+field.getType().getSimpleName()+" "+field.getName()+";\n");}s.append("}");System.out.println(s);}}/*反编译Students类的输出结果:public final class String{private final char[] value;private int hash;private static final long serialVersionUID;private static final ObjectStreamField[] serialPersistentFields;public static final Comparator CASE_INSENSITIVE_ORDER;}*/
外部修改属性值
package reflect.bean;import java.lang.reflect.Field;/*通过反射机制去访问对象属性,包括对对象属性值的获取和修改这种方法的好处是能写到配置文件中,能发挥反射机制的灵活性*/public class ReflectTest07 {public static void main(String[] args) throws Exception{Class studentClass = Class.forName("reflect.bean.Student");//注意:这个obj就是Student对象。效果等同 Student student = new Student();中的studentObject obj = studentClass.newInstance();//获取no属性,属性之间是按照属性名来区分的Field noField = studentClass.getDeclaredField("no");//给obj对象的no属性赋值/*虽然使用了反射机制,但是三要素还是缺一不可的:要素1:obj对象要素2:no属性要素3:属性值2222*/noField.set(obj,2222);//读取属性的值:2个要素:①对象 ②属性System.out.println(noField.get(obj)); //2222//可以从外部修改私有的属性吗Field nameField = studentClass.getDeclaredField("name");//打破封装!!!可能会给不法分子留下机会nameField.setAccessible(true);nameField.set(obj,"jackson");System.out.println(nameField.get(obj));}}
获取对象方法
先定义一个类,然后通过反射去拿取这个类的所有方法(签名)
package reflect.service;/*用户业务类*/public class UserService {public boolean login(String name,String password){if ("admin".equals(name)&&"123".equals(password)){return true;}return false;}public void logout(){System.out.println("系统已经安全退出");}}
package reflect;import java.lang.reflect.Method;import java.lang.reflect.Modifier;/*反射获取对象的方法*/public class ReflectTest08 {public static void main(String[] args) throws Exception{Class userServiceClass = Class.forName("reflect.service.UserService");//获取对象的所有方法Method[] declaredMethods = userServiceClass.getDeclaredMethods();//下面对方法进行遍历:for (Method declaredMethod : declaredMethods) {//获取方法的修饰符列表System.out.println(Modifier.toString(declaredMethod.getModifiers()));//获取方法的返回值类型System.out.println(declaredMethod.getReturnType());//获取方法名System.out.println(declaredMethod.getName());//获取方法的参数列表Class[] parameterTypes = declaredMethod.getParameterTypes();for (Class parameterType : parameterTypes) {System.out.println(parameterType.getSimpleName());}}}}/*结果输出:publicvoidlogoutpublicbooleanloginStringString*/
可变长参数
package reflect;/*可变长度参数int... args语法格式:类型...(注意,是3个点)1.可变长参数的参数个数是0-n个2.可变长参数必须在参数列表中的最后一个,且只能有一个可边长参数3.可变长参数*/public class ArgsTest {public static void main(String[] args) {m(); //m方法执行了...m(10); //m方法执行了...m(10,20,30,40); //m方法执行了...m3("ab","cd","ef"); //ab cd ef//可以传一个数组String[] strs = {"a","b","c"};m3(strs); //a b cm3(new String[]{"我","是","中","国","人"});//我 是 中 国 人}public static void m(int... args){System.out.println("m方法执行了...");}public static void m3(String... args){//args有length属性,说明args是一个数组//for (int i = 0; i < args.length; i++) {System.out.print(args[i]+"\t");}}}
反编译对象的方法
package reflect.service;/*用户业务类*/public class UserService {public boolean login(String name,String password){if ("admin".equals(name)&&"123".equals(password)){return true;}return false;}public void logout(){System.out.println("系统已经安全退出");}}
package reflect;import java.lang.reflect.Method;import java.lang.reflect.Modifier;/*反编译方法签名public class UserService {public boolean login(String name,String password){}}*/public class ReflectTest09 {public static void main(String[] args) throws Exception{StringBuilder s = new StringBuilder();Class userServiceClass = Class.forName("reflect.service.UserService");s.append(Modifier.toString(userServiceClass.getModifiers()));s.append(" class ");s.append(userServiceClass.getSimpleName()+"{\n");Method[] declaredMethods = userServiceClass.getDeclaredMethods();//public boolean login(String name,String password){}for (Method declaredMethod : declaredMethods) {s.append(" "+Modifier.toString(declaredMethod.getModifiers()));s.append(" "+declaredMethod.getReturnType()+" ");s.append(declaredMethod.getName());s.append("(");Class[] parameterTypes = declaredMethod.getParameterTypes();for (Class parameterType : parameterTypes) {s.append(parameterType.getSimpleName());s.append(",");}s.deleteCharAt(s.length()-1);s.append("){}\n");}s.append("}");System.out.println(s);}}/*输出如下:public class UserService{public void logout){}public boolean login(String,String){}}*/
访问或者修改对象的方法
package reflect.service;/*用户业务类*/public class UserService {public boolean login(String name,String password){if ("admin".equals(name)&&"123".equals(password)){return true;}return false;}public void logout(){System.out.println("系统已经安全退出");}}
package reflect;import reflect.service.UserService;import java.lang.reflect.Method;/*★★★★★重点:通过反射机制怎么去调用一个对象的方法?★★★★★反射机制让代码具有通用性,把需要改变的东西写到配置文件里,灵活*/public class ReflectTest10 {public static void main(String[] args) throws Exception{//不使用反射机制,如何调用方法?UserService userService = new UserService();boolean loginSuccess = userService.login("admin", "123");System.out.println(loginSuccess); //true//通过反射机制如何调用对象的方法?Class uService = Class.forName("reflect.service.UserService");//创建对象Object obj = uService.newInstance();//获取Method(方法名和形参列表)Method loginMethod = uService.getDeclaredMethod("login", String.class, String.class);//反射方法四要素:对象、方法名、实参、返回值Object retValue = loginMethod.invoke(obj, "admin", "123");System.out.println(retValue); //true}}
获取构造器信息
反编译构造器(烂尾工程)
package reflect.bean;public class Vip {int no;String name;String birth;boolean gender;public Vip(int no, String name, String birth, boolean gender) {this.no = no;this.name = name;this.birth = birth;this.gender = gender;}public Vip(int no, String name, String birth) {this.no = no;this.name = name;this.birth = birth;}public Vip(int no, String name) {this.no = no;this.name = name;}public Vip(int no) {this.no = no;}public Vip() {}}
package reflect;import java.lang.reflect.Constructor;import java.lang.reflect.Modifier;/*反编译一个类的Constructor构造方法*/public class ReflectTest11 {public static void main(String[] args) throws Exception{//public class Vip{}StringBuilder s = new StringBuilder();Class vipClass = Class.forName("reflect.bean.Vip");s.append(Modifier.toString(vipClass.getModifiers())+" class ");s.append(vipClass.getSimpleName()+"(");// Constructor[] declaredConstructors = vipClass.getDeclaredConstructors();// for (Constructor declaredConstructor : declaredConstructors) {// s.append("{\n\t"+Modifier.toString(declaredConstructor.getModifiers())+" class ");// s.append(vipClass.getSimpleName()+"(");//// s.append(")\n");// }s.append("){");s.append("}");s.append("\n}");System.out.println(s);}}
用反射初始化对象
package reflect.bean;public class Vip {int no;String name;String birth;boolean gender;public Vip(int no, String name, String birth, boolean gender) {this.no = no;this.name = name;this.birth = birth;this.gender = gender;}public Vip(int no, String name, String birth) {this.no = no;this.name = name;this.birth = birth;}public Vip(int no, String name) {this.no = no;this.name = name;}public Vip(int no) {this.no = no;}public Vip() {}@Overridepublic String toString() {return "Vip{" +"no=" + no +", name='" + name + '\'' +", birth='" + birth + '\'' +", gender=" + gender +'}';}}
package reflect;import reflect.bean.Vip;import java.lang.reflect.Constructor;/*使用反射机制创建对象*/public class ReflectTest12 {public static void main(String[] args) throws Exception{//不使用反射机制怎么创建对象?Vip v1 = new Vip();Vip v2 = new Vip(110, "zhangsan", "2001-10-1", true);//使用反射机制怎么创建对象//无参Class vipClass = Class.forName("reflect.bean.Vip");Object obj1 = vipClass.newInstance();//有参//第一步,先获取到这个有参数的构造方法Constructor constructor = vipClass.getDeclaredConstructor(int.class, String.class, String.class, boolean.class);//第二步,调用构造方法new对象Object obj2 = constructor.newInstance(110, "lisi", "1996-7-10", false);System.out.println("obj1:" + obj1 + "\nobj2:" + obj2);}}//输出://obj1:Vip{no=0, name='null', birth='null', gender=false}//obj2:Vip{no=110, name='lisi', birth='1996-7-10', gender=false}
获取一个类的父类及其实现的所有接口
package reflect;/*给你一个类,如何获取这个类的父类,以及实现了哪些接口?*/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(superclass.getName());//java.lang.Object//获取String类所实现的所有接口(一个类可以实现多个接口)Class[] interfaces = stringClass.getInterfaces();for (Class anInterface : interfaces) {System.out.println(anInterface.getName());}//java.io.Serializable//java.lang.Comparable//java.lang.CharSequence}}
重点笔记
Class studentClass = Class.forName("reflect.bean.Student");Field nameField = studentClass.getDeclaredField("name");//打破封装!!!可能会给不法分子留下机会nameField.setAccessible(true);nameField.set(obj,"jackson");Class uService = Class.forName("reflect.service.UserService");Method loginMethod = uService.getDeclaredMethod("login", String.class, String.class);Object obj = uService.newInstance();//反射方法四要素:对象、方法名、实参、返回值Object retValue = loginMethod.invoke(obj, "admin", "123");Class vipClass = Class.forName("reflect.bean.Vip");Constructor constructor = vipClass.getDeclaredConstructor(int.class, String.class, String.class, boolean.class);//第二步,调用构造方法new对象Object obj2 = constructor.newInstance(110, "lisi", "1996-7-10", false);
