概述
通过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获取value
String 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.Student
String simpleName = studentClass.getSimpleName();
System.out.println(simpleName); //Student
//获取类中所有public 修饰的Field
Field[] fields = studentClass.getFields();
System.out.println(fields.length); //1
//取出这个Field
Field f = fields[0];
//取出这个Field的名字
String fieldName = f.getName();
System.out.println(fieldName); //no
//获取类中所有Field
Field[] 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());
}
}
}
/*
整体内容输出如下:
------------------------------------
5
private String name
protected int age
boolean gender
public int no
public 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();中的student
Object 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());
}
}
}
}
/*
结果输出:
public
void
logout
public
boolean
login
String
String
*/
可变长参数
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 c
m3(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() {
}
@Override
public 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);