文章目录
反射机制
- 反射机制有什么用?
通过Java语言中的反射机制可以操作字节码文件
有点类似于黑客,可以读和修改字节码文件
通过反射机制可以操作代码片段( class文件)
- 反射机制的相关类在哪个包下?
java.lang.reflect.*;
- 反射机制相关的类有哪些?
java.lang.Class 代表整个字节码,代表一个类型
java.lang.reflect.Method 代表字节码中的方法字节码
java.lang.reflect.Constructor 代表字节码中的构造方法字节码
java.lang.reflect.Field 代表字节码中的属性字节码
反射中的Class就是指整个类,Field就是表示类中的字段,Constructor表示方法中的构造器,而Method表示类中的方法
获取Class的三种方式
Class.forName方法
1.是个静态方法
2.方法的参数是一个字符串
3.字符串需要的是一个完整的类名
4.完整类名必须带有包名
方式一:forName静态方法获得
public class ReflectTest01 {
public static void main(String[] args){
//方式一:
try {
Class c1=Class.forName("java.lang.String"); //c1代表String.class文件,或者说是C1代表String类型
Class c2=Class.forName("java.util.Date"); //c2代表Date类型
Class c3=Class.forName("java.lang.Integer"); //c3代表Integer类型
Class c4=Class.forName("java.lang.System");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
方式二:对象.getClass方法
import java.util.Date;
public class ReflectTest01 {
public static void main(String[] args){
//方式一:
Class c1=null;
Class c2=null;
Class c3=null;
try {
c1=Class.forName("java.lang.String"); //c1代表String.class文件,或者说是C1代表String类型
c2=Class.forName("java.util.Date"); //c2代表Date类型
c3=Class.forName("java.lang.Integer"); //c3代表Integer类型
Class c4=Class.forName("java.lang.System");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// java中任何一个对象都有一个方法:getClass方法
String s="abc";
Class x=s.getClass(); //x代表String.class 字节码文件,x代表String类型
System.out.println(c1==x); //true (==判断的是内存地址)
Date time=new Date();
Class y=time.getClass();
System.out.println(c2==y);// true
//c2和y两个变量中的保存的内存地址是一样的,都指向方法区中的字节码文件。
}
}
方式三:任何类型.class属性
import java.util.Date;
public class ReflectTest01 {
public static void main(String[] args){
//第三种方式:java语言中任何一种类型,包括基本数据类型 都有.class属性
Class z=String.class; // z代表String类型
Class k=Date.class; // k代表Date类型
Class f=int.class; //f代表int类型
Class e=double.class; // e代表double类型
}
}
利用反射实例化对象
通过Class的newInstance方法来实例化对象
newInstance方法内部实际上调用了无参数构造方法,必须保证无参构造存在才可以
public class ReflectTest02 {
public static void main(String[] args) throws Exception {
//不适用反射机制,创建对象
User user=new User();
System.out.println(user);
//使用反射来创建对象
//通过反射机制,获取Class,通过Class来实例化对象
Class c=Class.forName("Bean.User");
Object obj=c.newInstance();
//newInstance方法会调用User这个类的无参数构造方法,完成对象的创建.
// System.out.println(obj);
}
}
- 代码这么多,为什么要用反射
使用反射机制创建对象更加灵活
在不改变源代码的基础上,可以做到不同对象的实例化
非常灵活,符合OCP开闭原则(对扩展开放,对修改关闭)
在工作中经常使用的高级框架,
包括:
ssh ssm
Spring SpringMVC MyBatis
Spring Struts Hibernate
…
高级框架的底层实现原理都采用了反射机制,所以反射机制是很重要的.
灵活性演示
public static void main(String[] args) throws Exception{
//直接创建对象,直接写死了,只能是User对象,无法修改成其他对象
User user=new User();
//用反射读取配置文件创建对象,非常灵活,只需在配置文件中修改就可以创建任意对象
//通过IO流读取classinfo.properties文件
FileReader reader=new FileReader("src/main/resources/classinfo.properties");
//创建属性类对象Map
Properties pro=new Properties();
//加载
pro.load(reader);
//关闭流
reader.close();
//通过key获取value key value都是String
String className=pro.getProperty("className");
System.out.println(className);
//通过反射机制实例化对象
Class c=Class.forName(className);
Object obj=c.newInstance();
System.out.println(obj);
}
Class.forName方法
public class ReflectTest04 {
public static void main(String[] args) throws ClassNotFoundException {
//forName方法的执行必然导致类加载,类加载必然执行静态代码块
Class.forName("Bean.MyClass");
}
}
//另一个包下
public class MyClass {
//静态代码块在类加载的时候执行,且只执行一次
static{
System.out.println("MyClass类的静态代码开始执行");
}
}
- 如果你只希望一个类的静态代码块执行,其他代码都不执行,可以使用Class.forName(“完整类名”)
- 这个方法会导致类加载,类加载中静态代码块必然执行一次
反射获取Field
public static void main(String[] args) throws Exception{
//获取整个类
Class studentClass=Class.forName("Bean.Student");
Field[] fields= studentClass.getFields();
System.out.println(fields.length);
Field f=fields[0];
System.out.println(f.getName());
Field[] fs=studentClass.getDeclaredFields();
System.out.println(fs.length);
System.out.println("==========");
for(Field field: fs){
//获取属性的修饰符
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();
System.out.println(fName);
//获取属性的名字
System.out.println(field.getName());
}
}
反编译Field
public static void main(String[] args) throws ClassNotFoundException {
StringBuffer sb=new StringBuffer();
Class student = Class.forName("java.lang.Thread");
sb.append(Modifier.toString(student.getModifiers())+"class"+student.getSimpleName()+"{");
Field[] fields=student.getDeclaredFields();
for (Field field:fields
) {
sb.append("\t");
sb.append(Modifier.toString(field.getModifiers()));
sb.append(" ");
sb.append(field.getType());
sb.append(" ");
sb.append(field.getName());
sb.append(";\n");
}
sb.append("}");
System.out.println(sb);
}
利用反射访问类字段
- 如何修改字段的值
- 如何得到字段的值
这种写法虽然有些多,但是具有非常强的移植性,代码很灵活.
访问字段三要素:拿到对象,什么属性,设置/得到值
反射也不例外,也是按步骤得到三要素
public static void main(String[] args) throws Exception{
//不用反射给访问对象字段
Student s=new Student();
//给字段赋值
s.no=123;
//读取字段值
System.out.println(s.no);
//通过反射访问对象的字段:如何用反射给字段赋值,或者获取字段的值
Class student=Class.forName("Bean.Student");
Object obj=student.newInstance();//先获取一个实例,通过无参方法获取
//获取no字段
Field f1 = student.getDeclaredField("no");
f1.set(obj,2222);//obj这个对象的f1设置为2222
System.out.println(f1.get(obj));
//访问私有字段
Field f2=student.getDeclaredField("name");
Object obj1=student.newInstance();
//访问前需要打破封装 某个字段的封装
f2.setAccessible(true);
f2.set(obj1,"姓名");
System.out.println(f2.get(obj1));
}
插入知识点:可变长参数
- 可变长参数在参数列表中只能有一个,并且是在末尾位置
- 个数【0,n】
- 可以当成一个数组来看待
//可变长参数
public static void main(String[] args) {
// m1(123,3);
m2("asd",1,2,3,4,5);
}
public static void m1(int...args){
System.out.println("这是可变长参数");
}
public static void m2(String s,int...a){
System.out.println(s);
for(int x:a){
System.out.println(x);
}
}
使用反射机制来调用方法
public static void main(String[] args) throws Exception{
//不是使用反射调用方法
UserService userService=new UserService();
boolean logingS=userService.login("root","123");
//使用反射调用方法 拿到类,拿到实例,拿到方法,调用方法拿到处理值
Class user=Class.forName("Bean.UserService");
Object obj=user.newInstance();
Method login=user.getDeclaredMethod("login",String.class,String.class);
//调用这个方法,传递这个参数,返回这个值
Object retValue=login.invoke(obj,"root","123");
System.out.println(retValue);
}
使用反射创建对象
public static void main(String[] args) throws Exception {
//使用反射机制创建对象
//反射前两步都固定了,拿到类,拿到实例
//拿到反射类中的方法是类调用的,不是用类的实例调用的
Class c= Class.forName("Bean.User");
//调用无参数的构造方法 创建对象
Object obj=c.newInstance();
//用有参数的构造方法
Constructor con=c.getDeclaredConstructor(int.class,String.class);
Object newObj= con.newInstance(20,"783");
System.out.println(newObj);
}
版权声明:本文为Merciful_Lion原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。