Java基础24–反射的应用2&3

反射的应用2:动态创建对象

2:在运行时能够创建任意引用数据类型的对象**

  • 方式一:使用Class对象直接new对象
    步骤:
    (1)获取某个类型的Class对象
    (2)通过Class对象来创建这个Class所代表的类型的对象
@Test
	public void test01() throws ClassNotFoundException, InstantiationException, IllegalAccessException{
		//(1)获取某个类型的Class对象(四种方法选择合适的)   
		Class clazz = Class.forName("com.atguigu.ext.demo.AtGuiguDemo");
		
		//(2)创建对象
		//obj的编译时类型,Object类型
		//obj的运行时类型是AtGuiguDemo类型
		Object obj = clazz.newInstance();//这里的newInstance()没有参数,因为它是用无参构造创建实例
		System.out.println(obj);
	}

java 动态classpath 反射 java动态new 对象class_创建对象

( 注意:在这里加载AtGuiguDemo这个类时,会优先加载扩展类加载器)

有一个前提条件:有无参构造方法
若是将AtGuiguDemo无参构造去掉,会报错
当AtGuiguDemo没有无参构造时
java.lang.InstantiationException: com.atguigu.ext.demo.AtGuiguDemo
Caused by: java.lang.NoSuchMethodException: com.atguigu.ext.demo.AtGuiguDemo.()
解决方法:
a:加无参构造方法
b:方式二创建对象

  • 方式二:通过Class对象先获取有参构造,然后再创建对象
    步骤:
    (1)获取某个类型的Class对象
    (2)通过Class对象来获取Constructor对象
    (2)通过Constructor对象来创建这个Class所代表的类型的对象
@Test
	public void test02() throws Exception{
		//(1)获取某个类型的Class对象
		Class clazz = Class.forName("com.atguigu.ext.demo.AtGuiguDemo");
				
		/*
		 * (1)Constructor clazz.getConstructor(Class<?>... parameterTypes)某个公共的构造器
		 * (2)Constructor clazz.getDeclaredConstructor(Class<?>... parameterTypes)某个声明的构造器
		 * 为什么通过形参列表就可以确认哪个构造器呢?---一个类中可能存在多个构造器,但是多个构造器重载的话,形参列表一定不一样(名称一样),所以通过形参列表就可以唯一的定位到一个构造器
		 * 如果Class<?>... parameterTypes,一个都不传,即获取无参构造
		 */
		//(2)获取有参构造对象
		Constructor c = clazz.getConstructor(int.class,String.class,int.class);
		
		//(3)通过Constructor对象来创建实例对象
		Object obj = c.newInstance(1,"尚硅谷",10);//这里的newInstance(实参列表),因为它用有参构造创建对象
		System.out.println(obj);
	}

java 动态classpath 反射 java动态new 对象class_java 动态classpath 反射_02


若是不知道声明的构造器形参列表,比如框架中spring创建对象时是怎样知道构造器里面的形参列表的呢?–需要自己配置告诉它(告诉它类名、构造器形参列表等),不然不知道,除非能得到所有的构造器,否则没办法知道

若是构造器私有化了,再运行会报错:找不到这个形参方法,应该用:

Constructor c = clazz.getDeclaredConstructor(int.class,String.class,int.class);
@Test
	public void test02() throws Exception{
		//(1)获取某个类型的Class对象
		Class clazz = Class.forName("com.atguigu.ext.demo.AtGuiguDemo");
				
	
		//(2)获取有参构造对象
		Constructor c = clazz.getDeclaredConstructor(int.class,String.class,int.class);
		
		//(3)通过Constructor对象来创建实例对象
		Object obj = c.newInstance(1,"尚硅谷",10);//这里的newInstance(实参列表),因为它用有参构造创建对象
		System.out.println(obj);
	}

还是报错:报非法的访问接近异常,不能访问构造器

c.setAccessible(true);//设置私有构造器可以访问
@Test
	public void test02() throws Exception{
		//(1)获取某个类型的Class对象
		Class clazz = Class.forName("com.atguigu.ext.demo.AtGuiguDemo");
				
		/*
		 * (1)Constructor clazz.getConstructor(Class<?>... parameterTypes)某个公共的构造器
		 * (2)Constructor clazz.getDeclaredConstructor(Class<?>... parameterTypes)某个声明的构造器
		 * 一个类中可能存在多个构造器,但是多个构造器重载的话,形参列表一定不一样,所以通过形参列表就可以唯一的定位到一个构造器
		 * 如果Class<?>... parameterTypes,一个都不传,即获取无参构造
		 */
		//(2)获取有参构造对象
		Constructor c = clazz.getDeclaredConstructor(int.class,String.class,int.class);
		
		c.setAccessible(true);//设置私有构造器可以访问
		
		//(3)通过Constructor对象来创建实例对象
		Object obj = c.newInstance(1,"尚硅谷",10);//这里的newInstance(实参列表),因为它用有参构造创建对象
		System.out.println(obj);
	}

成功创建对象

java 动态classpath 反射 java动态new 对象class_java 动态classpath 反射_03

@Test
	public void test03(){
//		AtGuiguDemo s = new AtGuiguDemo(2,"xx",20);//The constructor AtGuiguDemo(int, String, int) is not visible
//		System.out.println(s);
	}

私有化构造器不能直接这样使用

反射的应用3:动态的设置或获取属性的值

3、在运行时可以为任意对象的任意属性赋值,或者获取任意对象的任意属性的值

Field类:
  (1)set(Object obj, Object value)
  (2)Object get(Object obj)
  (3)setAccessible(true)

建议大家编写类时保留无参构造。(大部分情况下使用无参构造),原因:
(1)创建对象方便
(2)继承时也方便
子类构造器默认调用父类的无参构造
(3)反射创建对象也方便

步骤:
(1)获取某个类型的Class对象
(2)创建实例对象
(3)为某个属性赋值
①先获取某个属性Field对象
②设置属性可访问
属性对象.setAccessible(true)
③为属性赋值

@Test
	public void test01() throws Exception{
		//(1)获取某个类型的Class对象
		Class clazz = Class.forName("com.atguigu.ext.demo.AtGuiguDemo");
		
		//(2)创建对象
		//obj的编译时类型,Object类型
		//obj的运行时类型是AtGuiguDemo类型
		Object obj = clazz.newInstance();//这里的newInstance()没有参数,因为它是用无参构造创建实例
		System.out.println(obj);
		
		//(3)为属性赋值
//		①获取id属性的Field对象
		/*
		 * 		Field clazz.getField(name)  获取公共的
				Field clazz.getDeclaredField(name) 获取声明的
		 */
		Field idField = clazz.getDeclaredField("id");
		
		//设置id属性可访问
		idField.setAccessible(true);//一般属性private修饰
		
//		②为id属性赋值
		/*
		 * 回忆之前,如何为属性赋值
		 *    对象.属性名 = 值
		 * 
		 * 属性的特点:(1)每一个对象的属性是独立(2)属性有默认值
		 * 所以在为属性赋值时,要说为哪个对象的属性赋值
		 */
//		idField.set(obj, value);//obj是代表AtGuiguDemo的对象,value代表值
		idField.set(obj, 3);
		System.out.println(obj);
		
		//(4)为info属性赋值
		//①先获取info属性对象
		Field infoField = clazz.getDeclaredField("info");
//		②设置info属性可访问
		infoField.setAccessible(true);
		//③为info属性赋值
		infoField.set(obj, "尚硅谷");
		System.out.println(obj);
		
		//(5)获取num属性的值
		//①先获取num属性的Field对象
		Field numField = clazz.getDeclaredField("num");
		//②设置num属性可以访问
		numField.setAccessible(true);
		//③获取num属性值
		/*
		 * 回忆之前,获取属性的值
		 * 	变量 = 对象名.属性名
		 */
//		numField.get(obj);//obj就是代码哪个对象的属性
		Object value = numField.get(obj);
		System.out.println(value);
	}

java 动态classpath 反射 java动态new 对象class_创建对象_04