文章目录
Java-反射基础
一、概念:
- 将类的各个组成部分(变量、方法、构造器)封装为对象(即:Class、Constructor、Field、Method对象)
- 在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
- 对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
优点:
- 可以在程序运行过程中,操作这些对象。
- 可以解耦,提高程序的可扩展性。
缺点:效率较低
二、4个反射相关对象
1)Class对象
在程序运行期间,Java运行时系统始终为所有的对象维护一个称为运行时的类型标识,该信息跟踪着每个对象所属的类,虚拟机利用运行时类型信息选择相应的方法执行。保存这些信息的类称为Class
//获取Class对象
1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
* 多用于配置文件,将类名定义在配置文件中。读取文件,加载类
2. 类名.class:通过类名的属性class获取
* 多用于参数的传递
3. 对象.getClass():getClass()方法在Object类中定义着。
* 多用于对象的获取字节码的方式
* 结论:
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
Class对象功能
* 获取功能:
1. 获取成员变量们
* Field[] getFields() :获取所有public修饰的成员变量
* Field getField(String name) 获取指定名称的 public修饰的成员变量
* Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
* Field getDeclaredField(String name) 同上,获取指定名称的域
2. 获取构造方法们
* Constructor<?>[] getConstructors()
* Constructor<T> getConstructor(Class<?>... parameterTypes)
* Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
* Constructor<?>[] getDeclaredConstructors()
3. 获取成员方法们:
* 获取从父类得到的public方法,以及自己的public方法[不包括default,private
* Method[] getMethods()
* Method getMethod(String name, Class<?>... parameterTypes)
* 获取声明的所有方法[private , public ,default
* Method[] getDeclaredMethods()
* Method getDeclaredMethod(String name, Class<?>... parameterTypes)
* 参数类型:
第一个:方法的名称
第二个:参数类型的Class对象
4. 获取全类名
* String getName()
方法演示:
package pers.xu.others;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author a_apple
* @create 2020-05-19 8:59
*/
class Person {
int parentDefaultAge = 12;
public String parentPubName = "123";
private String parentPrivate = "no";
public void showParentPublic() {
System.out.println("public method --parent");
}
private void showParentPrivate() {
System.out.println("private method --parent");
}
void showParentDefault() {
System.out.println("default method --parent");
}
}
class Student extends Person {
int stuDefaultId = 1232;
public String stuPublicName = "TomStu";
private String stuPrivateName = "haha";
void showStuDefault() {
System.out.println("I am student");
}
public void showStuPublic() {
System.out.println("pub");
}
private void showStuPrivate() {
System.out.println("pri");
}
}
public class TestClass {
public static void main(String[] args) {
Student stu = new Student();
Class<? extends Student> stuClass = stu.getClass();
System.out.println("------------------------getMethods()");
// getMethods():获取从父类得到的public方法,以及自己的public方法[不包括default,private
for (Method method : stuClass.getMethods()) {
System.out.println("method: " + method.getName());
}
System.out.println("-----------------------getDeclaredMethods()");
// 获取自己的所有方法 default,public,private
for (Method declaredMethod : stuClass.getDeclaredMethods()) {
System.out.println("dm: " + declaredMethod.getName());
}
System.out.println("-----------------------getFields()");
// 获取父类的public属性和自己的public属性
for (Field field : stuClass.getFields()) {
System.out.println("field: " + field.getName());
}
System.out.println("-------------------------getDeclaredFields()");
// 获取自己的所有属性public,default,private
for (Field field : stuClass.getDeclaredFields()) {
System.out.println("field: " + field.getName());
}
}
}
输出:
------------------------getMethods()
method: showStuPublic
method: showParentPublic
method: wait
method: wait
method: wait
method: equals
method: toString
method: hashCode
method: getClass
method: notify
method: notifyAll
-----------------------getDeclaredMethods()
dm: showStuDefault
dm: showStuPublic
dm: showStuPrivate
-----------------------getFields()
field: stuPublicName
field: parentPubName
-------------------------getDeclaredFields()
field: stuDefaultId
field: stuPublicName
field: stuPrivateName
Process finished with exit code 0
2)Field对象
* 操作:
1. 设置值
* void set(Object obj, Object value)
* 参数类型:
第一个参数:被操作的对象
第二个参数:设定的值
2. 获取值
* get(Object obj)
3. 忽略访问权限修饰符的安全检查
* setAccessible(true):暴力反射
3)Constructor对象
* 创建对象:
* T newInstance(Object... initargs)
* 如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法
3. 忽略访问权限修饰符的安全检查
* setAccessible(true):暴力反射
4)Method对象
* 执行方法:
* Object invoke(Object obj, Object... args)
*参数类型:
第一个:代表要调用这个方法的对象
第二个:该方法的参数
* 获取方法名称:
* String getName:获取方法名
3. 忽略访问权限修饰符的安全检查
* setAccessible(true):暴力反射
三、反射用法举例
1)通过反射运行配置文件内容
不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
提供2个类:
public class UserService1 {
public void service1(){
System.out.println("UserService1 的 service1方法...");
}
}
public class UserService2 {
//...
public void service2(){
System.out.println("UserService2 的 service2执行了...");
}
}
配置文件:reflection.properties
# 类名需要提供完整的包名路径
className = com.test.UserService2
methodName = service2
测试:
package com.fanshe;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* @author a_apple
* @create 2019-10-20 9:39
*/
public class ReflectDemo {
public static void main(String[] args) throws Exception{
//1.读取reflection.properties文件
Properties pro = new Properties();
InputStream stream = ReflectDemo.class.getClassLoader().getResourceAsStream("reflection.properties");
pro.load(stream);
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//2.通过反射创建对象
Class<?> class1 = Class.forName(className);
Object instance = class1.newInstance();
System.out.println(instance);
//3.获取方法执行---提供的是无参方法
Method method = class1.getMethod(methodName);
method.invoke(instance);
}
}
输出:
com.test.UserService2@29453f44
UserService2 的 service2执行了...
2)反射略过泛型检查
向ArrayList<Integer>
集合里添加一个String
类型的值
因为:泛型检测是在编译期进行的,而反射是在运行时候进行的,对.class文件进行操作
public class GenericIgnore {
public static void main(String[] args) throws Exception {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
//list.add("hello"); //这个是会报编译错的
//但是下面可以让list添加String类型
Class<?> listClass = list.getClass();
Method addMethod = listClass.getMethod("add", Object.class); //传一个Object,此时可以加Integer,String...
addMethod.invoke(list, "字符串");
System.out.println(list);
}
}
输出:
[2, 4, 1, 字符串]
3)JBDC加载Mysql驱动
public class JdbcDemo {
public static void main(String[] args) {
//初始化驱动
try {
//驱动类com.mysql.jdbc.Driver
//就在 mysql-connector-java-5.0.8-bin.jar中
//如果忘记了第一个步骤的导包,就会抛出ClassNotFoundException
Class.forName("com.mysql.jdbc.Driver");
System.out.println("数据库驱动加载成功 !");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
部分引用其他博主,侵删