文章目录
- 反射
- 反射机制有什么用?
- 相关的类在哪些包中?
- 反射机制相关的重要的类?
- Class类的理解
- 类加载过程的理解
- 获取Class的四种方法
- 通过读属性文件实例化对象
- `Class.Name("")`
- 获取绝对路径
- 以流的形式直接返回
- ResourceBundle资源绑定器
- 类加载器(ClassLoader)
- 双亲委派机制
- 获取Field(属性)
- 获取Field案例
- 反编译获取Field
- 反射机制访问对象属性
- 可变长度参数
- 获取Method(方法)
- 反编译获取Method
- 反射机制调用方法
- 获取Constructor(构造方法)
- 反编译获取Constructor
- 调用 无参/有参构造方法
- invoke调用方法
- `setAccessible(true)`访问私有属性和函数
- 命令执行
- 获取父类、所有实现的接口
- `@Override`注解
反射
反射机制有什么用?
通过反射机制可以操作字节码文件
相关的类在哪些包中?
java.lang.reflect.*
反射机制相关的重要的类?
java.lang.Class:代表整个字节码,代表一个类型,代表整个类
java.lang.reflect.Method:代表字节码中的方法字节码。代表类中的方法。
java.lang.reflect.Constructor:代表字节码中的构造方法字节码。代表类中的构造方法
java.lang.reflect.Field:代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)
Class类的理解
1、类的加载过程:
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。
加载到内存中的类,我们就称为运行时类,此运行时类就作为Class的一个实例
2、加载到内存中的运行时类,会缓存一定时间。在此时间之内,我们可以通过不同的方式来获取此运行时类,但此运行时类不会加载第二次(获取的内存地址是一致的)
类加载过程的理解
- 加载
将class文件字节码内容加载到虚拟机内存中,并在方法区中生成一个·java.lang.Class对象,作为方法区中类数据的方法入口(即引用地址) - 链接
2.1 验证:作为链接阶段的第一步,验证的主要作用就是确保被加载的类的正确性 ,说白了也就是我们加载好的.class文件不能对我们的虚拟机有危害,所以先检测验证一下
2.2 准备:准备阶段主要为类变量分配内存并设置初始值,这里的初始值是指数据类型的默认值。这些变量所使用的内存都将在方法区中进行分配。
public class Vip{
public static int num = 10;
}
以上代码在准备阶段过后num值是0,值为10时是经过初始化阶段后
数据类型的默认值有:
2.3 解析阶段:将常量池中的符号引用替换为直接引用的过程
- 初始化
执行类构造器< clinit >()方法的过程。类加载机制的最后一步。类构造器是构造类信息的,不是构造该类对象的构造器。
当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化
获取Class的四种方法
第一种方式
Class.forName() 将字节码文件加载进内存,返回Class对象
1.静态方法
2.方法的参数是一个字符串
3.字符串需要的是一个完整类名
4.完整的类名必须带有包名,java.lang包也不能省略
try {
Class c1 = Class.forName("java.lang.String"); //c1代表String类型
Class c2 = Class.forName("java.util.Date"); //c1代表Data类型
Class c3 = Class.forName("java.lang.Integer"); //c1代表Integer类型
Class c4 = Class.forName("java.lang.System"); //c1代表System类型
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String s1 = "abc";
Class x = s1.getClass(); //第二种方式:getclass()获取任何一个运行时类的类对象
Class y = String.class; //第三种方法:调用运行时类的属性。java语言中任何一种类型,包含数据类型,都有.class属性
ClassLoader ClassLoad = Reflect01.class.getClassLoader(); //第四种方法:类加载器ClassLoader
Class c = ClassLoad.loadClass("java.lang.String");
System.out.println(c);
通过读属性文件实例化对象
package Reflect;
import java.io.FileReader;
import java.util.Properties;
/*
反射机制灵活性的体现。
代码不需要改动,可以修改配置文件,即可创建出不同的实例对象
*/
public class Reflect03 {
public static void main(String[] args) throws Exception {
//通过IO流读取classinfo.properties文件
FileReader reader = new FileReader("Reflect\\classinfo");
//创建属性类对象Map
Properties pro = new Properties(); //key value都是String
//加载
pro.load(reader);
//关闭流
reader.close();
//通过key:className获取value
String className = pro.getProperty("className");
System.out.println(className);
//通过反射机制实例化对象
Class c = Class.forName(className);
Object obj = c.newInstance();
System.out.println(obj);
}
}
classinfo.properties文件
className=java.util.Date
Class.Name("")
这个方法的执行会导致类加载,类加载时,静态代码块执行。因此Class.Name("")能只让静态代码块执行
package Reflect;
/*
Class.forName("完整类名");
这个方法的执行会导致类加载,类加载时,静态代码块执行
*/
public class Reflect04 {
public static void main(String[] args) {
try {
Class.forName("Reflect.MyClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class MyClass{
static {
System.out.println("静态代码块执行");
}
public static void main(String[] args) {
System.out.println("Main方法执行");
}
}
获取绝对路径
Thread.currentThread().getContextClassLoader().getResource("相对路径的文件名").getPath();
package Reflect;
public class AboutPath {
/*
前提:这个文件必须在类路径下(src下,src是类的根路径,是从src目录作为起点开始)
Thread.currentThread() 当前线程对象
getContextClassLoader() 线程对象的方法,可以获取到当前线程的类加载器对象
getResource() 【获取资源】这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源
*/
public static void main(String[] args) throws Exception{
String path = Thread.currentThread().getContextClassLoader().getResource("Testinfo.properties").getPath(); //Testinfo.properties放在src
System.out.println(path);//得到绝对路径: /E:/IDEA2020/app/src/out/production/app/Testinfo.properties
String path2 = Thread.currentThread().getContextClassLoader().getResource("Reflect/classinfo.properties").getPath();//classinfo.properties放在src/Reflect下
System.out.println(path2);
}
}
以流的形式直接返回
以后要通过上面的方法来获取文件的绝对路径,而不是自己写一个路径上去了,这样才有更高的耦合性、适用性
package Reflect;
import java.io.FileReader;
import java.io.InputStream;
import java.util.Properties;
public class Reflect05 {
public static void main(String[] args) throws Exception {
/*方法一:使用getResource().getPath()先获取文件名,再通过FileReader创建流
//获取绝对路径
String path = Thread.currentThread().getContextClassLoader().getResource("Reflect/classinfo.properties").getPath();
System.out.println(path);
//创建读取流
FileReader reader = new FileReader(path);
*/
/*
方法二:使用getResourceAsStream直接以流的形式返回
*/
InputStream reader = Thread.currentThread().getContextClassLoader().getResourceAsStream("Reflect/classinfo.properties");
//创建属性类对象Map
Properties pro = new Properties();
//以简单的面向行的格式从输入字符流中读取属性列表(键和元素对)
pro.load(reader);
reader.close();
//在属性列表中搜索className键的属性
String className = pro.getProperty("className");
System.out.println(className);
}
}
ResourceBundle资源绑定器
java.util包下提供一个资源绑定器。便于获取属性配置文件中的内容。属性配置文件xx.properties必须放到类路径下
一:资源绑定器,只能绑定xx.properties文件,并且这个文件必须在类路径下
二:所获取文件的后缀必须是properties。但扩展名不需要写
package Reflect;
import java.util.ResourceBundle;
public class ResourceBundleTest {
public static void main(String[] args) {
ResourceBundle bundle = ResourceBundle.getBundle("classinfo");
String className = bundle.getString("className");
System.out.println(className);
}
}
类加载器(ClassLoader)
加载指的是将所需类的class文件读入到内存,并为之创建一个java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象。
JDK自带了三个类加载器
1. 启动类加载器 启动类加载器专门加载:/lib/rt.jar rt.jar中都是JDK最核心的类库
2. 扩展类加载器 扩展类加载器专门加载:/lib/ext/*.jar
3. 应用类加载器 应用类加载器专门加载:专门加载classpath中的类
package Reflect;
public class ClassLoaderTest {
public static void main(String[] args) {
//对于自定义类,使用应用类加载器(AppClassLoader)进行加载
ClassLoader classload1 = ClassLoaderTest.class.getClassLoader(); //getClassLoader()返回类的类加载器。
System.out.println(classload1); //sun.misc.Launcher$AppClassLoader@18b4aac2
//调用应用类加载器的getParent():获取扩展类加载器(ExtClassLoader)
ClassLoader classloader2 = classload1.getParent();
System.out.println(classloader2); //sun.misc.Launcher$ExtClassLoader@7d4991ad
//调用扩展类加载器的getParent():无法获取启动类加载器
//引导类加载器主要负责加载java的核心库,无法加载自定义类
ClassLoader classloader3 = classloader2.getParent();
System.out.println(classloader3); //null
}
}
双亲委派机制
优先从启动类加载器中加载,这个称为"父"。“父"无法加载到,再从扩展类加载器中加载,这个称为"母”。双亲委派。
如果都加载不到,才会考虑从应用类加载器中加载,直到加载到为止
获取Field(属性)
Field[] getFields() :获取所有public修饰的成员变量
Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
Field getField(String name) 获取指定名称的 public修饰的成员变量
Field getDeclaredField(String name) 获取指定的成员变量
- 获取Field前是要先获取整个类的:
Class StudentClass = Class.forName("Reflect.Student");
- 获取Student类的类名:
String ClassName = StudentClass.getName();
System.out.println("完整类名"+ClassName); //Reflect.Student
String ClassSimpleName = StudentClass.getSimpleName();
System.out.println("简易类名"+ClassSimpleName); //Student
- 输出所有
public修饰的字段(Field),返回一个数组对象
Field[] fields = StudentClass.getFields(); 表示返回一个数组
fields[0].getName() 表示输出数组中第一个对象的名称
- 输出所有的字段。这包括public,protected,默认,private字段,但不包括继承的字段。返回一个数组对象
Field[] fs = StudentClass.getDeclaredFields();
- 输出字段的属性类型:
Class fieldType = i.getType(); 返回类对象,该对象标识字段对象表示的字段的声明类型。
System.out.println("属性的类型:"+fieldType.getSimpleName());
- 输出字段的修饰符:
public int getModifiers()
以整数编码,返回对应修饰符的数字标识。 其中有:public , protected , private , final , static , abstract和interface
Modifier.toString() 返回数字标识所对应的字符串。
获取Field案例
package Reflect;
//反射属性Field
public class Student {
// Field 表示属性/成员
public String name;
protected int age;
boolean sex;
private int no;
public static final double PI = 3.1415926;
}
package Reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class Reflect06 {
public static void main(String[] args) throws Exception{
//获取整个类
Class StudentClass = Class.forName("Reflect.Student");
String ClassName = StudentClass.getName();
System.out.println("完整类名"+ClassName); //Reflect.Student
String ClassSimpleName = StudentClass.getSimpleName();
System.out.println("简易类名"+ClassSimpleName); //Student
//获取类中所有public修饰的Field getFields()返回一个数组
Field[] fields = StudentClass.getFields();
System.out.println(fields.length); //2
//获取Filed数组中第一个成员变量的名字 getName()
Field f = fields[0];
String fileName = f.getName();
System.out.println(fileName); //name
System.out.println("==============");
Field[] fs = StudentClass.getDeclaredFields();
for (Field i: fs ) {
int num = i.getModifiers();//getModifiers()返回Filed修饰符的代号,以整数编码
System.out.println(num);
String modifierString = Modifier.toString(num); //将数字代号转成字符串 静态方法:Modifier.toString()
System.out.println("修饰符:"+modifierString);
Class fieldType = i.getType();
System.out.println("属性的类型:"+fieldType.getSimpleName()); //获取属性的类型 getSimpleName()
}
}
}
反编译获取Field
package Reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/*
通过反射机制,反编译一个类的属性Field
*/
public class Reflect07 {
public static void main(String[] args) throws Exception{
//用于拼接字符串
StringBuilder s = new StringBuilder();
Class StudentClass = Class.forName("Reflect.Student");
//获取Student类的所有属性
Field[] fs = StudentClass.getDeclaredFields();
s.append(Modifier.toString(StudentClass.getModifiers()) + " class " + StudentClass.getSimpleName()+"{\n");
for ( Field f:fs ) {
s.append("\t");
s.append(Modifier.toString(f.getModifiers())); //属性修饰符
s.append(" ");
s.append(f.getType().getSimpleName()); //属性type
s.append(" ");
s.append(f.getName()); //属性的名称
s.append(";\n");
}
s.append("\n}");
System.out.println(s);
}
}
反射机制访问对象属性
package Reflect;
import java.lang.reflect.Field;
public class Reflect08 {
public static void main(String[] args) throws Exception{
//反射机制创建obj对象(Student对象)
Class Stu = Class.forName("Reflect.Student");
Object obj = Stu.newInstance();
//获取name属性(public类型),返回Field类型
Field nameField = Stu.getField("name");
//set给name属性赋值
nameField.set(obj,"DMIND");
//读取属性的值
String name =nameField.get(obj).toString();
System.out.println(name);
}
}
可变长度参数
比如:int... args
- 可变长度参数要求的参数个数是:0~N个
- 可变长度参数在参数列表中必须在最后一个位置上,而且可变长度参数只能出现一个
- 可变长度参数可以当作一个数组看待
package Reflect;
public class Reflect09 {
public static void main(String[] args) {
m(1,"a","b","c");
m(2,"你","ok?","?");
}
public static void m(int a, String... args){
for (String s : args) {` //获取方法名
System.out.println(method.getName());`
System.out.println(s);
}
}
}
获取Method(方法)
Method getMethod(String name, 类<?>... parameterTypes) //返回该类所声明的public方法
Method getDeclaredMethod(String name, 类<?>... parameterTypes) //返回该类所声明的所有方法
//第一个参数获取该方法的名字,第二个参数获取标识该方法的参数类型
Method[] getMethods() //获取所有的public方法,包括类自身声明的public方法,父类中的public方法、实现的接口方法
Method[] getDeclaredMethods() // 获取该类中的所有方法
获取所有类型的方法,返回一个Method数组
Class Stu = Class.forName("Reflect.UserService");
Method[] methods = Stu.getDeclaredMethods();
获取修饰符列表
System.out.println(Modifier.toString(method.getModifiers())); //public static
获取方法的返回值类型
System.out.println(method.getReturnType().getSimpleName()); //boolean
获取方法名
System.out.println(method.getName()); //login
获取方法的参数修饰符
Class[] parameterType = method.getParameterTypes();
for (Class parm:parameterType ) {
System.out.println(parm.getSimpleName());
}
案例:
package Reflect;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class Reflect10 {
public static void main(String[] args) throws Exception{
Class Stu = Class.forName("Reflect.UserService");
Method[] methods = Stu.getDeclaredMethods();
for (Method method:methods ) {
//获取修饰符列表
System.out.println(Modifier.toString(method.getModifiers()));
//获取方法的返回值类型
System.out.println(method.getReturnType().getSimpleName());
//获取方法名
System.out.println(method.getName());
//获取方法的参数修饰符
Class[] parameterType = method.getParameterTypes();
for (Class parm:parameterType ) {
System.out.println(parm.getSimpleName());
}
}
}
}
反编译获取Method
package Reflect;
/*
反编译获取Method
*/
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class Reflect11 {
public static void main(String[] args) throws Exception{
StringBuilder s = new StringBuilder();
Class UserService = Class.forName("Reflect.UserService");
s.append(Modifier.toString(UserService.getModifiers())+" class "+UserService.getSimpleName()+" {\n");
Method[] methods = UserService.getMethods();
for (Method m:methods ) {
s.append("\t"+Modifier.toString(m.getModifiers()));
s.append(" ");
s.append(m.getReturnType().getSimpleName());
s.append(" ");
s.append(m.getName());
s.append("(");
Class[] parms = m.getParameterTypes();
for (int i = 0; i < parms.length; i++) {
s.append(parms[i].getSimpleName());
if(i==parms.length-1){
}else s.append(",");
}
s.append("){}\n");
}
s.append("}");
System.out.println(s);
}
}
反射机制调用方法
package Reflect;
import java.lang.reflect.Method;
public class Reflect12 {
public static void main(String[] args) throws Exception {
//创建对象
Class UserService = Class.forName("Reflect.UserService");
Object obj = UserService.newInstance();
//获取login方法
Method loginMethod = UserService.getDeclaredMethod("login", String.class, String.class);
//使用invoke()调用login方法
Object retValue = loginMethod.invoke(obj,"admin","123");
System.out.println(retValue);
}
}
获取Constructor(构造方法)
Constructor<?>[] getConstructors() :只返回public构造函数
Constructor<?>[] getDeclaredConstructors() :返回所有构造函数
Constructor<> getConstructor(类<?>... parameterTypes) : 匹配和参数配型相符的public构造函数
Constructor<> getDeclaredConstructor(类<?>... parameterTypes) : 匹配和参数配型相符的构造函数
反编译获取Constructor
package Reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
public class Reflect13 {
public static void main(String[] args) throws Exception{
StringBuilder s = new StringBuilder();
Class vipConstruct = Class.forName("Reflect.Vip");
//获取头
s.append(Modifier.toString(vipConstruct.getModifiers())+" class "+vipConstruct.getSimpleName()+"{\n");
Constructor[] constructors = vipConstruct.getDeclaredConstructors();
//获取构造方法
for (Constructor constructor:constructors ) {
s.append("\t");
s.append(Modifier.toString(constructor.getModifiers()));
s.append(" ");
s.append(vipConstruct.getSimpleName());
s.append("(");
Class[] parameterTypes = constructor.getParameterTypes();
//获取参数列表的类型
for (int i = 0; i < parameterTypes.length; i++) {
s.append(parameterTypes[i].getSimpleName());
if (i < parameterTypes.length-1){
s.append(",");
}
}
s.append("){}\n");
}
s.append("}");
System.out.println(s);
}
}
调用 无参/有参构造方法
package Reflect;
import java.lang.reflect.Constructor;
public class Reflect14 {
public static void main(String[] args) throws Exception {
Class vip = Class.forName("Reflect.Vip");
//调用无参构造
Object noarg = vip.newInstance();
System.out.println(noarg);
//调用有参构造
//根据参数类型确定哪个有参构造方法 --> newInstance()创建对象
Constructor con = vip.getDeclaredConstructor(int.class,String.class);
Object obj = con.newInstance(10,"DMIND");
System.out.println(obj);
}
}
invoke调用方法
public Object invoke(Object obj, Object... args)
第一个参数是类的实例对象,第二个参数为相应函数中的参数
如果调用的这个方法是普通方法,第一个参数就是类对象;
如果调用的这个方法是静态方法,第一个参数就是类;
package MyClassLoader;
import java.lang.reflect.Method;
public class invokeTest {
public void Hello(){
System.out.println("DMIND");
}
public static void main(String[] args) throws Exception{
//获取类对象
Class c = Class.forName("MyClassLoader.invokeTest");
//获取实例对象
Object obj = c.newInstance();
//获取Hello方法
Method method = c.getMethod("Hello");
method.invoke(obj); //输出DMIND
}
}
setAccessible(true)访问私有属性和函数
在一般情况下,我们使用反射机制不能对类的私有private字段进行操作,绕过私有权限的访问。但一些特殊场景存在例外的时候,比如我们进行序列化操作的时候,需要去访问这些受限的私有字段,这时我们可以通过调用AccessibleObject上的setAccessible()方法来允许访问。
Java.lang.reflect.AccessibleObject类是Field,Method和Constructor类对象的基类,可以提供将反射对象标记为使用它抑制摸人Java访问控制检查的功能, 同时上述的反射类中的Field,Method和Constructor继承自AccessibleObject。 所以我们在这些类方法基础上调用setAccessible()方法,既可对这些私有字段进行操作。
命令执行
示例一:
调用java.lang.Runtime下的exec(String)来命令执行
package MyClassLoader;
import java.lang.reflect.Constructor;
public class RuntimeTest {
public static void main(String[] args) throws Exception {
//获取类对象
Class runtime = Class.forName("java.lang.Runtime");
//获取Runtime的构造方法
Constructor constructor = runtime.getDeclaredConstructor();
constructor.setAccessible(true);
runtime.getMethod("exec", String.class).invoke(constructor.newInstance(),"calc");
}
}
示例二:
package MyClassLoader;
import java.lang.reflect.Method;
public class RuntimeTest {
public static void main(String[] args) throws Exception {
Class c1 = Class.forName("java.lang.Runtime");
Method method = c1.getMethod("exec",String.class);
Method RuntimeMethod = c1.getMethod("getRuntime");
Object obj = RuntimeMethod.invoke(c1);
method.invoke(obj,"calc");
}
}
将示例二代码压缩一下:
Class c1 = Class.forName("java.lang.Runtime"); c1.getMethod("exec", String.class).invoke(c1.getMethod("getRuntime").invoke(c1),"/System/Applications/Calculator.app/Contents/MacOS/Calculator");
获取父类、所有实现的接口
package Reflect;
public class Reflect15 {
public static void main(String[] args) throws Exception {
Class StringClass = Class.forName("java.lang.String");
Class superclass = StringClass.getSuperclass(); //获取父类
System.out.println(superclass.getName()); //父类的名称
//获取String类所实现的所有接口
Class[] interfaces = StringClass.getInterfaces();
for (Class in:interfaces ) {
System.out.println(in.getName());
}
}
}
@Override注解
标识性注解,给编译器做参考的
编译器看到方法上有@Override注解,会自动检查改方法是否重写了父类的方法。如果没有重写,报错
这个注解知识再编译阶段起作用,和运行期无关
@Override
public String toString() {
return super.toString();
}