Java反射基础
1 反射简介
1.1 什么是反射
java反射机制是在运行状态中,可以获取任意一个类的结构 属性,创建对象,得到方法,执行方法。在这种运行状态动态获取信息以及动态调用对象方法的功能被称为java语言的反射机制。
1.2 反射的缺点
性能开销:由于反射涉及动态解析的类型,因此无法执行某些 Java 虚拟机优化。
破坏封装性: 反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。
内部曝光: 由于反射允许代码执行在非反射代码中非法的操作,例如访问私有字段和方法,这可能会导致代码功能失常并可能破坏可移植性。
2 反射机制
2.1 类加载过程
类加载过程:
1:java文件通过java编译器编译后,在磁盘中产生 .class文件。.class是二进制文件,通过jvm可以被识别。
2:jvm通过类加载器读取字节码文件,取出二进制数据,加载到内存中,解析 .class文件中的内容。.
3:加载结束后,JVM 开始进行连接阶段(包含验证、准备、初始化)。经过这一系列操作,类的变量会被初始化。
2.2 class对象
想使用反射,就必须获取先要使用该类对应的class对象。在java中,无论一个类生成多少个对象,这些对象都会对应一个 class对象,这个class对象是通过JVM生成的,通过class对象能够获取整个类的结构。
例如: Person person = new Person();
1:JVM加载的时候,当 new Person() 时,JVM会根据 Person的全限定类名去加载一个User.class文件。
2:JVM 会去本地磁盘查找 User.class 文件并加载 JVM 内存中。
3:JVM通过类加载器会创建User类的class对象。一个类只有一个class对象
2.3 方法的反射调用
方法的反射调用,也就是 Method.invoke 方法。
public static void main(String[] args) throws Exception{
Class c=Class.forName("reflect_01.Person");
//获取类的构造方法
Constructor con=c.getConstructor();
//创建了对象
Object obj=con.newInstance();
//获取单个方法:show
Method m1=c.getMethod("show");
//参数1:哪个对象要执行 参数2:可变参数
//public Object invoke(Object obj,Object...args)
m1.invoke(obj); //show方法调用了
}
public class Person {
public void show() {
System.out.println("show方法调用了");
}
}
3 使用反射
3.1 java.lang.reflect 包
java.lang.reflect 包的核心接口和类如下:
Member 接口:反映关于单个成员(字段或方法)或构造函数的标识信息。
Field 类:提供一个类的域的信息以及访问类的域的接口。
Method 类:提供一个类的方法的信息以及访问类的方法的接口。
Constructor 类:提供一个类的构造函数的信息以及访问类的构造函数的接口。
Array 类:该类提供动态地生成和访问 JAVA 数组的方法。
Modifier 类:提供了 static 方法和常量,对类和成员访问修饰符进行解码。
Proxy 类:提供动态地生成代理类和类实例的静态方法
3.2 获取 Class 对象
获取class对象的三种方式
1)Class.forName 静态方法(最常用的方式)
public static class forName(String classNanme)//需要全路径
使用类的完全限定名来反射对象的类。
package reflect_01;
public class ReflectDome1 {
public static void main(String[] args) throws ClassNotFoundException {
Class c1 = Class.forName("reflect_01.ReflectDome1");//全路径
System.out.println(c1.getCanonicalName());//reflect_01.ReflectDome1
Class c2=Class.forName("reflect_01.ReflectDome1");//全路径
System.out.println(c2.getCanonicalName());//reflect_01.ReflectDome1
System.out.println(c1 == c2);//true ,说明指向的是同一个class对象
}
}
2)类名 + .class
package reflect_01;
public class ReflectDome1 {
public static void main(String[] args) throws ClassNotFoundException {
Class c3=ReflectDome1.class;
System.out.println(c3.getCanonicalName());//reflect_01.ReflectDome1
}
}
3)object类的getClass方法
package reflect_01;
public class ReflectDome1 {
public static void main(String[] args) throws ClassNotFoundException {
//方式1
ReflectDome1 p1 = new ReflectDome1();
Class c1 = p.getClass();
ReflectDome1 p2 = new ReflectDome1();
Class c2 = p2.getClass();
System.out.println(p1 == p2);//false
System.out.println(c1 == c2);//true, 指向的是同一个class对象
System.out.println(c.getCanonicalName());//reflect_01.ReflectDome1
System.out.println(c2.getCanonicalName());//reflect_01.ReflectDome1
}
}
3.3 创建实例
1)通过class对象 获取一个类的构造方法
package reflect_01;
public class ReflectDome3 {
public static void main(String[] args) throws Exception {
//获取字节码文件对象
Class c=Class.forName("reflect_01.Person");
//获取带参构造器
// public Constructor<T> getConstructor(Class<?>...parameterTypes)
Constructor con=c.getConstructor(String.class,int.class,String.class);//获取这样的构造方法,明确了类型
//通过带参构造方法对象创建对象
// public T newInstance(Object...initargs)
Object obj=con.newInstance("lh",29,"北京");
System.out.println(obj);//Person [name=鹿晗, age=29, addree=北京]
}
}
public class Person {
public String name;
public int age;
public String addree;
public Person(String name,int age,String addree) {
this.name=name;
this.age=age;
this.addree=addree;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", addree=" + addree + "]";
}
}
2)Constructor 创建对象
常用方法:
newInstance(Object... para)
调用这个构造方法, 把对应的对象创建出来
参数: 是一个Object类型可变参数, 传递的参数顺序 必须匹配构造方法中形式参数列表的顺 序!
setAccessible(boolean flag)
如果flag为true 则表示忽略访问权限检查 !(可以访问任何权限的方法)
3.4 Field
getFiled - 根据名称获取公有的(public)类成员。
getDeclaredField - 根据名称获取已声明的类成员。但不能得到其父类的类成员。
getFields - 获取所有公有的(public)类成员。
getDeclaredFields - 获取所有已声明的类成员。
package reflect_01;
//person类
public class Person {
private String name;
int age;
public String addree;
public Person() {
}
Person(String name,int age){
this.name=name;
this.age=age;
}
private Person(String name){
this.name=name;
}
public Person(String name,int age,String addree) {
this.name=name;
this.age=age;
this.addree=addree;
}
public void show() {
System.out.println("show111");
}
// 无参构造
public void method(String s) {
System.out.println("method"+s);
}
// 带参带返回值
public String getString(String s,int i) {
return s+"---"+i;
}
private void functon() {
System.out.println("functon");
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", addree=" + addree + "]";
}
package reflect_01;
public class ReflectDome5 {
public static void main(String[] args) throws Exception {
Class c=Class.forName("reflect_01.Person");
Constructor con=c.getDeclaredConstructor();
Object obj=con.newInstance();
//获取所有成员变量
Field[] fields=c.getDeclaredFields();
for(Field field:fields) {
System.out.println(field);
}
//输出结果
//private java.lang.String reflect_01.Person.name
//int reflect_01.Person.age
//public java.lang.String reflect_01.Person.addree
//获取address并对其赋值
Field addressField=c.getField("addree");
// 将指定对象变量上field,设值
addressField.set(obj, "北京");// 该obj对象的addressfield字段设置值为北京
System.out.println(obj);
//对name赋值 ,私有
Field nameField=c.getDeclaredField("name");
//暴力访问,私有就可以访问,忽略权限检查
nameField.setAccessible(true);
//赋值
nameField.set(obj, "lh");
System.out.println(obj);
//对age赋值
Field ageField=c.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(obj, 18);
System.out.println(obj);
}
//输出结果
//Person [name=null, age=0, addree=北京]
//Person [name=lh, age=0, addree=北京]
//Person [name=lh, age=18, addree=北京]
}
}
3.5 Method
- getMethod(String methodName , class… clss)
根据参数列表的类型和方法名, 得到一个方法(public修饰的) - getMethods();
得到一个类的所有方法 (public修饰的) - getDeclaredMethod(String methodName , class… clss)
根据参数列表的类型和方法名, 得到一个方法(除继承以外所有的:包含私有, 共有, 保护, 默认) - getDeclaredMethods();
得到一个类的所有方法 (除继承以外所有的:包含私有, 共有, 保护, 默认)
public class ReflectDome6 {
public static void main(String[] args) throws Exception {
Class c=Class.forName("reflect_01.Person");
//获取类的构造方法
Constructor con=c.getConstructor();
//创建了对象
Object obj=con.newInstance();
//获取单个方法:show
Method m1=c.getMethod("show");
//参数1:哪个对象要执行 参数2:可变参数
// public Object invoke(Object obj,Object...args)
m1.invoke(obj);
//获取类的方法
Method m3=c.getMethod("method",String.class);
m3.invoke(obj, "hello");
Method m4=c.getMethod("getString",String.class,int.class);
String s=(String)m4.invoke(obj, "hello",100);
System.out.println(s);//字符串
Method m5=c.getDeclaredMethod("functon");//获取所有的私有方法
m5.setAccessible(true);//暴力破解
m5.invoke(obj);
}
}
3.6 Constructor
getConstructor - 返回类的特定 public 构造方法。参数为方法参数对应 Class 的对象。
getDeclaredConstructor - 返回类的特定构造方法。参数为方法参数对应 Class 的对象。
getConstructors - 返回类的所有 public 构造方法。
getDeclaredConstructors - 返回类的所有构造方法。
在这里插入代码片
3.6 破解访问权限限制
可以使用 Constructor.setAccessible(true) 来绕开 Java 语言的访问权限限制。
public static void main(String[] args) throws Exception {
//获取字节码文件对象
Class c=Class.forName("reflect_01.Person");
//获取私有构造方法
// public Constructor[] getDelaredConstructor : 返回所有构造方法
Constructor con=c.getDeclaredConstructor(String.class); //所有权限的构造方法
con.setAccessible(true);// 值为true,表示反射的对象在使用时应该取消Java语言访问检查
Object obj=con.newInstance("lh");
System.out.println(obj);
}
最后,如果有问题,希望指正,一起进步。