前情提要
本文内容来着老杜的Java零基础教程视频(适合Java 0基础,Java初学入门)_哔哩哔哩_bilibili
作者只是将其知识进行提炼和二次整合
基础知识
-
作用:反射机制可以操作字节码文件,可以操作代码片段(class文件)
-
相关类所在包:java.lang.reflect.*
-
反射机制相关且重要的类
-
java.lang.Class:代表字节码文件(代表整个字节码、一个类型、整个类)
-
java .lang .reflect .Method:代表字节码中的方法字节码(类中的方法)
-
java.lang.reflect.Constructor:代表字节码中的构造字节码(类中的构造方法)
-
java.lang.reflect.Field:代表字节码中的属性字节码(类中的属性)
-
获取class的三种方式
-
要操作一个类的字节码,首先得获得这个类的字节码,以下有获取class的三种方式
-
Class.forname("字符串")
-
静态方法
-
方法的参数是一个完整类名的字符串
-
必须带有包名(java.lang包也不能省略)
-
在类名中右键可以复制完整类名
-
-
该方法的执行会导致类加载,可以通过该方法调用类的静态代码块(类加载是静态代码块执行)
-
只是希望该类的静态代码块执行,且其它代码不执行,就可以使用该方法
-
-
-
对象.getClass()
-
任何对象都有getClass()方法,是Object类实现好的
-
-
类型.class
-
java语言中任何一种类型,包括基本数据类型,都有class属性
-
-
-
同一个类型的Class对象指向同一内存单元
Class c1=null; try { c1=Class.forName("java.lang.String");//代表String.class文件或者是String类型 } catch (ClassNotFoundException e) { e.printStackTrace(); } String word="abbc"; Class<? extends String> x = word.getClass();//代表的也是String.class文件或者是String类型 System.out.println(c1==x);//结果为true
.
!!!通过反射实例化对象
-
反射对象.newInstance():会调用反射对象对应类型的无参数构造方法,完成对象的创建
-
如果定义了有参构造而没有定义无参,会报实例化异常
-
-
反射实例化对象的优点:可以通过外部配置文件中设置的类型(属性名=完整的类名),动态的创建指定类型的对象
-
在不改变java源代码的基础上,可以实现不同对象的实例化(符合ocp开闭原则:对扩展开放,对修改关闭)
-
FileReader fileReader = new FileReader("reflect/src/type.properties"); Properties properties = new Properties();//获取配置信息对象map, k v都是String properties.load(fileReader);//加载 fileReader.close(); //通过反射机制获取对象 String classType = properties.getProperty("className");//通过key获取value System.out.println(classType.getClass());
读取配置文件相关
获取文件的绝对路径
-
通用方式获取文件绝对路径(前提是该文件放在类路径下【src是类的根路径】)
-
可以跨系统,跨平台,不会收到环境移植的影响
String path = Thread.currentThread().getContextClassLoader(). getResource("以类的根路径作为起点的文件相对路径").getPath()
-
Thread.currentThread() 当前线程对象
-
getContextClassLoader() 是线程对象的方法,可以获取到当前线程的类加载器对象
-
getResource() 【获取资源】这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源
-
getPath():获取加载资源的绝对路径
-
以流的形式返回
InputStream is = Thread.currentThread().getContextClassLoader(). getResourceAsStream("以类的根路径作为起点的文件相对路径")
资源绑定器读取属性配置文件
-
使用资源绑定器,属性配置文件必须放在类路径下,且必须以properties为后缀
ResourceBundle bundle=ResourceBundle.getBundle("type"); String className = bundle.getString("className");//根据属性名获取属性值 System.out.println(className);
获取属性
获取所有属性相关信息
-
field对象包含属性修饰符、属性类型和属性名
-
获取所有属性对象
-
Class对象.getFields():获取所有公共属性组成的数组
-
Class对象.getDeclaredFields():获取所有属性组成的数组
-
-
获取属性名
-
属性对象.getName()
-
-
获取属性类型
-
属性对象.getType():返回属性类型的Class对象
-
获取类名
-
Class对象.getName():获取完整类名
-
Class对象.getSimpleName():获取简类名(不带包名)
-
-
-
获取属性或类的修饰符
-
field/Class对象.getModifiers():返回修饰符的代号
-
Modifier.toString(代号):将代号转成字符串
-
反编译(了解)
-
通过java文件获取源码
-
代码
-
使用StringBuilder是为了拼接字符串
-
StringBuilder builder = new StringBuilder(); Class<?> aClass = Class.forName("java.lang.String"); builder.append(Modifier.toString(aClass.getModifiers())+" class "+ aClass.getSimpleName()+"{\n"); Field[] fields = aClass.getDeclaredFields();//获取该类对象的属性 for (Field field : fields) { builder.append(Modifier.toString(field.getModifiers())+" "+field.getName()+"\n"); } builder.append("}"); System.out.println(builder);
-
运行结果
.
!!!反射机制访问属性
非私有属性访问
-
Class对象.getDeclaredField("属性名"):通过属性名获取属性对象
-
属性对象.set(对象,属性值):给对象的该属性赋值
-
属性对象.get(对象):获取对象的该属性的值
Class<?> student = Class.forName("Student"); Object stu = student.newInstance();//实例化对象 Field age = student.getDeclaredField("age"); age.set(stu,5555);//给对象的age属性赋值 System.out.println(age.get(stu));
私有属性访问
-
私有属性无法直接赋值,根据上述步骤会报无效访问异常
.
-
属性对象.setAccessible():打破封装,可以访问私有属性
Class<?> student = Class.forName("Student"); Object stu = student.newInstance();//属性实例化 Field name = student.getDeclaredField("name"); name.setAccessible(true); name.set(stu,"打破成功"); System.out.println(name.get(stu));
.
获取方法
获取所有方法相关信息
-
Class对象.getDeclaredMethods():获取所有方法
-
方法对象.getReturnType():获取方法返回值类型
-
方法对象.getParameterTypes():获取方法所有的参数类型
-
获取方法的修饰符、方法名与获取属性的基本一致
反编译(了解)
-
方法体(业务逻辑)无法反编译
-
代码
-
builder.deleteCharAt("下标"):删除指定下标位置的字符
-
StringBuilder builder = new StringBuilder(); Class<?> aClass = Class.forName("java.lang.String"); Method[] methods = aClass.getDeclaredMethods();//获取该类对象的所有方法 for (Method method : methods) { builder.append(Modifier.toString(method.getModifiers())+" " +method.getReturnType()+" "+method.getName()+ "(" ); for(Class type:method.getParameterTypes()){ //获取参数类型 builder.append(type.getSimpleName()+","); } if(builder.substring(builder.length()-1).equals(",")){ //判断最后一个字符是否为多出来的“,”,如果是就删除 builder.deleteCharAt(builder.length()-1); } builder.append("){}\n"); } System.out.println(builder);
-
运行结果
.
!!!反射机制调用方法
-
getDeclaredMethod(方法名,接收参数类型的可变参数):获取指定方法对象
-
!!!方法对象.invoke(对象,方法名,参数列表):调用对象的指定方法,返回结果
Class<?> student = Class.forName("Student"); Object stu = student.newInstance();//对象实例化 Method check = student.getDeclaredMethod("check", String.class, String.class);//根据方法名和参数列表定位具体方法 Object result = check.invoke(stu, "liao", "123");//调用对象的check方法,并传入参数 System.out.println(result);//输出返回结果 /**check方法内容**/ public Boolean check(String username,String password){ return username.equals("liao")&&password.equals("123"); }
.
获取构造器(了解)
反编译
-
除了没有方法返回值,反编译构造方法和反编译方法基本一致
StringBuilder builder = new StringBuilder(); Class<?> aClass = Class.forName("Student"); Constructor[] methods = aClass.getConstructors();//获取该类对象的所有方法 for (Constructor method : methods) { builder.append(Modifier.toString(method.getModifiers())+" " +method.getName()+ "(" ); Class[] types=method.getParameterTypes(); for(Class type:types){ //获取参数类型 builder.append(type.getSimpleName()+","); } if(types.length>0){ //参数列表大于0的时候删除多余的逗号 builder.deleteCharAt(builder.length()-1); } builder.append("){}\n"); } System.out.println(builder);
反射机制调用构造器方法
-
调用无参构造方法
-
Class对象.newInstance()
-
Class对象.getDeclaredConstructor()获取无参构造方法对象之后,调用newInstance()
-
-
调用有参构造方法
-
Class对象.getDeclaredConstructor(参数类型列表):获取有参构造方法对象
-
构造方法对象.newInstance(参数列表)
-
!!!获取父类和接口
-
Class对象.getSuperClass():获取父类
-
Class对象.getInterfaces():获取所有实现的接口
扩展(与反射无关)
类加载器(了解)
-
定义:专门负责加载类的命令/工具
-
jdk自带的三个类加载器
-
启动类(父加载器)
-
扩展类(母加载器)
-
应用类
-
-
执行案例
String s ="abc"
-
代码在开始执行之前,会将所需要类全部加载到JVM当中
-
通过类加载器加载,看到以上代码类加载器会找string.class文件,找到就加载
-
首先通过“启动类加载器"”加载
-
启动类加载器专门加载:
jdk1.8.0 101\jre\lib\rt.jar
(rt.jar中都是JDK最核心的类库)
-
-
如果通过“启动类加载器"加载不到的时候,会通过"扩展类加载器"加载
-
扩展类加载器专门加载:
jdk1.8.0 101\jre\lib\ext\*.jar
-
-
如果“扩展类加载器"没有加载到,那么会通过“应用类加载器”加载
-
应用类加载器专门加载: classpath中的类
-
-
-
-
双亲委派机制
-
java中为了保证类加载的安全,使用了双亲委派机制
-
优先从启动类加载器中加载,这个称为“父"
-
"父"无法加载到,再从扩展类加载器中加载这个称为“母”
-
如果都加载不到才会考虑从应用类加载器中加载
-
-
可变长度参数
-
可变长参数
-
接收参数个数是0~N个
-
在参数列表中必须放在最后一个位置且只能有一个
-
可以看作是数组,也可以接收一个数组
-
-
语法:类型...