初步了解反射

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 类的加载器

参考以下

类加载器详解_Dongguo丶的博客-CSDN博客_类加载器类加载器的分类JVM支持两种类型的类加载器,分别为引导类加载器(BootstrapClassLoader)和自定义类加载器(User-Defined ClassLoader)从概念上来讲, 自定义类加载器一般指的是程序中由开发人员自定义的一类,类加载器,但是Java虚拟机规范却没有这么定义,而是将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器无论类加载器的类型如何划分,在程序中我们最常见的类加载器始终只有3个,如下所示:所以具体为引导类加载器(BootstrapClassLhttps://blog.csdn.net/m0_37450089/article/details/119962669

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;
	}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值