1.类的加载和初始化
要了解反射,首先要了解以下概念。
1.1类加载
当程序使用某个类的时候,如果此时该类还没有被加载到内存中,系统会通过三个步骤对该类进行初始化(个人理解区别于动态初始化)。三个步骤(阶段)分别为
(1)加载:load
就是指将类型的clas字节码数据读入内存
(2)连接:link
①验证:校验合法性等
②准备:准备对应的内存(方法区),创建Class对象,为类变量赋默认值,为静态常量赋初始值。
③解析:把字节码中的符号引用替换为对应的直接地址引用
(3)初始化:initialize(类初始化)即执行<clinit>类初始化方法,大多数情况下,类的加载就完成了类的初始化,有些情况下,会延迟类的初始化。
1.2 类的初始化
以下操作会导致类的初始化:
1.运行main方法,所在的类会初始化
2.动态初始化时,也就是new一个对象时如果该类还未初始化会先初始化该类
3.调用静态变量和方法
4.当一个类的子类初始化时会初始化还未初始化的父类
5.通过反射操作一个还未初始化的类
注意:
*通过子类调用父类的静态变量或者静态方法,那么表示为对父类的主动使用,而不是子类的主动使用
* 静态变量或者静态方法定义在谁身上就表示对谁的主动使用,而不看调用方,所以子类并不会被初始化
public class Parent {
static int a=10;
static void f1(){
System.out.println("FFFFFFFF");
}
}
class son extends Parent{
static int b=15;
}
class Test{
public static void main(String[] args) {
System.out.println(son.a);
son.f1();
}
}
如以上代码 输出为
10
FFFFFFFF
1.3 类的加载器
参考以下
2 反射
所谓反射,就是对于一个类,无论加载或未加载都可以任意获取和调用这个类的方法和属性,而这些都需要先获取该类的Class对象。
2.1 如何获取Class对象
(1)类型名.class
要求编译期间已知类型
(2)对象.getClass()
获取对象的运行时类型
(3)Class.forName(类型全名称)
可以获取编译期间未知的类型
(4)ClassLoader的类加载器对象.loadClass(类型全名称)
可以用系统类加载对象或自定义加载器对象加载指定路径下的类型
(Class c1=loader.loadClass( 相应的加载器路径/要加载的类))
2.2 反射的应用
举例 操作任意类型的属性
public class TestField {
public static void main(String[] args)throws Exception {
//1、获取Student的Class对象
Class clazz = Class.forName("com.atguigu.test.Student");
//2、获取属性对象,例如:id属性
Field idField = clazz.getDeclaredField("id");
//3、如果id是私有的等在当前类中不可访问access的,我们需要做如下操作
idField.setAccessible(true);
//4、创建实例对象,即,创建Student对象
Object stu = clazz.newInstance();
//5、获取属性值
/*
* 以前:int 变量= 学生对象.getId()
* 现在:Object id属性对象.get(学生对象)
*/
Object value = idField.get(stu);
System.out.println("id = "+ value);
//6、设置属性值
/*
* 以前:学生对象.setId(值)
* 现在:id属性对象.set(学生对象,值)
*/
idField.set(stu, 2);
value = idField.get(stu);
System.out.println("id = "+ value);
}
操作方法同理,有提供的获取方法和调用方法的方法
(1)获取该类型的Class对象 Class clazz = Class.forName(......);
(2)获取方法对象 Method method = clazz.getDeclaredMethod(方法名:"setName",形参的Class对象:String.class);
(3)创建实例对象 Object obj = clazz.newInstance();
(4)调用方法 Object result = method.invoke(obj,"字符串");
2.3 为什么要用反射,使用反射的好处
例1 可以灵活调用不同类的方法和属性,只需要修改已经写好的配置文件
className=Reflection.Person
methodName=eat
public static void main(String[] args) throws Exception{
//加载配置文件
//创建Properties对象
Properties pro=new Properties();
//获取class目录下的配置文件
pro.load(ReflectionText.class.getClassLoader().getResourceAsStream("pro.properties"));
//获取配置文件定义的数据
String className=pro.getProperty("className");
String methodName=pro.getProperty("methodName");
//加载该类进内存
Class cls = Class.forName(className);
//执行方法
cls.getMethod(methodName).invoke(cls.newInstance());
例 2
用未知类型的集合接收返回的SQL数据 写数据库查询工具类用到
上代码
protected <T> ArrayList<T> getList(Class<T> clazz,String sql,Object... args) throws Exception{
//1、获取链接对象
Connection conn = JDBCTools.getConnection();
//2、编写sql,由形参传入
//3、获取PreparedStatement对象
PreparedStatement pst = conn.prepareStatement(sql);
//4、设置?,由形参传入
if(args!=null && args.length>0){
for (int i = 0; i < args.length; i++) {
//数组的下标从0开始,pst的?的序号是从1开始,所以这里用i+1
pst.setObject(i+1, args[i]);
}
}
//5、执行sql
ResultSet rs = pst.executeQuery();
/*
* 如何把ResultSet结果集中的数据变成一个一个的Javabean对象,放到ArrayList对象,并且返回
*/
ArrayList<T> list = new ArrayList<>();
/*
* 要从ResultSet结果集中获取一共有几行,决定要创建几个对象
* 要从ResultSet结果集中获取一共有几列,决定要为几个属性赋值
* ResultSet结果集对象中,有一个方法ResultSetMetaData getMetaData()获取结果集的元数据
* 元数据就是描述结果集中的数据的数据,例如:列数,列名称等
*/
ResultSetMetaData metaData = rs.getMetaData();
int count = metaData.getColumnCount();//获取列数
while(rs.next()){//循环一次代表一行,就要创建一个Javabean对象
//(1)创建一个Javabean对象
T t = clazz.newInstance();//这个方法有要求,要求Javabean这个类要有无参构造
//(2)设置对象的属性值
/*
* 反射操作属性的步骤:
* ①获取Class对象,现在有了
* ②获取属性对象Field
* Field f = clazz.getDeclaredField("属性名");
* ③创建Javabean对象,已经创建
* ④设置属性的可访问性 setAccessible(true)
* ⑤设置属性的值
*/
for (int i = 0; i < count; i++) {//一共要为count个属性赋值
// Field f = clazz.getDeclaredField("属性名");
String fieldName = metaData.getColumnLabel(i+1);//获取第几列的字段名
Field f = clazz.getDeclaredField(fieldName);
f.setAccessible(true);
f.set(t, rs.getObject(i+1));//rs.getObject(i+1)获取第几列的值
}
//(3)把Javabean对象放到list中
list.add(t);
}
pst.close();
rs.close();
//这里不关闭conn,因为它在同一个事务的其他地方还要使用
return list;
}