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);
}
( 注意:在这里加载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);
}
若是不知道声明的构造器形参列表,比如框架中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);
}
成功创建对象
@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);
}