一、反射
说明:若不学习框架或者自定义一些工具类,那么反射在开发中基本没啥用!在框架中主要应用的就是反射和动态代理的方式,比如Spring框架中的两大核心之一的IOC控制反转,就是利用反射的方式,应用的设计模式中的工厂设计模式的思想将对象的创建交给Spring框架进行管理,我们只需要进行配置就可以了,在使用的时候可以直接通过@AutoWired注解进行注入就可以使用了。使得我们开发的时候由面向对象上升为面向接口的方式进行开发。
1.1 反射概述
我们在使用IEDA的时候,只要得到某一个类的对象,然后使用.,它就会弹出这个类中所有的方法,给用户编写代码一个提示!
反射: 反射是一种机制,利用该机制可以在程序运行过程中对类进行解剖并操作类中的所有成员(成员变量,成员方法,构造方法)
1.1.1 反射过程
* 反射过程描述:
首先会有一个java文件,然后会编译成.class文件,字节码文件(会里面包含了当前这个类里面所有数据信息)会加载到内存,JVM负责管理,为了方便管理,它会将每一个加载进来的class文件生成一个对应的Class对象<对应的类型就是生成字节码之前java文件对应的类名类型>,这个class对象它就代表对应的类(java文件),那么我们可以通过这个类对应的class对象获得这个类中的数据信息(成员变量、成员方法、构造方法)
结论:我们使用反射,其实就是获得某个类的Class对象,然后通过这个Class对象对这个类中的成员数据进行处理(调用执行)!
1.1.2 反射前提
我们必须得到这类的字节码文件(.class文件) ====>>> Class对象
Class:它是一个类,Class类的实例表示正在运行的Java应用程序中的类和接口。我们的一个类最终在内存中都是字节码,然后JVM会对每一个类对应的字节码生成一个Class对象!然后通过这个Class对象就可以访问类中的所有信息!
* Java类与Class对象的对应关系:
一个类 =======>>> 一个Class对象
类中的一个成员变量 ======>>>> 一个Field对象
类中的一个成员方法 ======>>>> 一个Method对象
类中的一个构造方法 ======>>>> 一个Constructor对象
1.2 如何获得类对应的Class对象
1.2.1 方式一:通过Class类的静态方法
// 参数:某个类的全限定类名(包含包名和类名)
static Class<?> forName(String className) // 返回与给定字符串名称的类或接口相关联的Class对象。
Class<?> clazz = Class.forName("com.itheima.demo01_反射.Student"); // 给定类的全限定类名书写错误,会导致异常!ClassNotFoundException
1.2.2 方式二:通过类的对象的getClass方法
// 先获得到某个类的对象
Student student = new Student();
// 通过对象调用方法获得Class对象!
Class<? extends Student> clazz1 = student.getClass(); // getClass()方法是从Object继承过来的
1.2.3 方式三:通过类名的静态属性class
// 通过类名.class获得Class对象!
Class<Student> clazz = Student.class;
1.2.4 Class对象的常用功能
* String getSimpleName(); // 获得类名字符串:类名
* String getName(); // 获得类全名:包名+类名
* T newInstance() ; // 创建Class对象关联类的对象(公有构造方法)
public static void main(String[] args) throws Exception{
// 获得Student类对应的Class对象
Class<?> clazz = Class.forName("com.itheima.demo01_反射.Student");
System.out.println(clazz.getSimpleName()); // Student
System.out.println(clazz.getName()); // com.itheima.demo01_反射.Student
Object obj = clazz.newInstance(); // com.itheima.demo01_反射.Student@1647C
}
1.3 通过Class对象操作构造方法
1.3.1 获得Constructor对象
Constructor<?>[] getConstructors() // 通过Class对象获得类中所有的公有的构造方法对象
Constructor<?>[] getDeclaredConstructors() // 通过Class对象获得类中所有的构造方法对象(公有私有,全都要)
Constructor<T> getConstructor(Class<?>... parameterTypes) // 通过Class对象获得类中指定公有的构造方法对象! 【★★★★★】
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) // 通过Class对象获得类中指定的构造方法!(公有私有,全都要)
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
// 获得Student类对应的Class对象
Class<?> clazz = Class.forName("com.itheima.demo01_反射.Student");
/通过Class对象获得Student类中构造方法对应的Constructor对象!///
// 获得类中所有的公共的构造方法对应的对象数组
Constructor<?>[] constructors = clazz.getConstructors();
System.out.println(Arrays.toString(constructors)); //
// 获得类中所有的构造方法对应的对象数组(包括私有的!)
Constructor<?>[] dc = clazz.getDeclaredConstructors();
System.out.println(Arrays.toString(dc));
// 获得类中指定构造方法对应的对象
//Constructor<?> c1 = clazz.getConstructor();
Constructor<?> c1 = clazz.getDeclaredConstructor();
System.out.println(c1);
Constructor<?> c2 = clazz.getDeclaredConstructor(String.class, int.class);
System.out.println(c2);
}
1.3.2 通过构造方法对象创建类的实例对象
T newInstance(Object... initargs) 使用由此 Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。
public static void main(String[] args) throws Exception{
// 获得Student类对应的Class对象
Class<?> clazz = Class.forName("com.itheima.demo01_反射.Student");
// 获得Student类中指定的构造方法对象!
Constructor<?> constructor = clazz.getConstructor();
// 根据构造方法对象创建Student类的实例对象!
Object o = constructor.newInstance();
System.out.println(o); // com.itheima.demo01_反射.Student@14ae5a5
Student s = (Student) o;
}
注意:一旦类中的构造方法使用private修饰,那么通过构造方法对象创建了的实例对象就要发生变化!
public static void main(String[] args) throws Exception{
// 获得Student类对应的Class对象
Class<?> clazz = Class.forName("com.itheima.demo01_反射.Student");
// 获得Student类中指定的构造方法对象!
Constructor<?> constructor = clazz.getDeclaredConstructor(); // 有变动!
// 暴力访问!
constructor.setAccessible(true); // 有变动!
// 根据构造方法对象创建Student类的实例对象!
Object o = constructor.newInstance();
System.out.println(o); // com.itheima.demo01_反射.Student@14ae5a5
Student s = (Student) o;
}
4 通过Class对象操作成员方法对象
1.4.1 获得成员方法的Method对象
Method getMethod(String name, Class<?>... parameterTypes) // 返回指定公有的方法对象! 【★★★★★】
Method[] getMethods() // 返回所有公有的方法对象构成的数组,包括由类或接口声明的对象以及从超类和超级接口继承的类。
Method getDeclaredMethod(String name, Class<?>... parameterTypes)// 返回指定的方法对象!(私有的也可以玩)
Method[] getDeclaredMethods() // 返回所有的方法对象构成的数组,包括public,protected,default(package)访问和私有方法,但不包括继承方法。
public static void main(String[] args) throws Exception{
// 获得Student类对应的Class对象
Class<?> clazz = Class.forName("com.itheima.demo01_反射.Student");
// 获得Student类以及父类(接口)中所有的公有成员方法
Method[] methods1 = clazz.getMethods();
for (Method method : methods1) {
System.out.println(method);
}
System.out.println("====================================");
// 获得Student类中所有的成员方法(包含私有)
Method[] methods2 = clazz.getDeclaredMethods();
for (Method method : methods2) {
System.out.println(method);
}
System.out.println("====================================");
// 获得指定方法(公有无参)
Method show1 = clazz.getMethod("show1");
// 获得指定方法(私有无参)
Method show2 = clazz.getDeclaredMethod("show2");
// 获得指定方法(公有带参数)
Method method1 = clazz.getMethod("method1", String.class);
// 创建类的对象
Object obj = clazz.getConstructor().newInstance();
// 获得成员方法对象,期目的是为了执行这个成员方法
Object result = show1.invoke(obj);// 参数1:当前方法所属对象! 参数2:show1方法的参数值(若定义show1方法没有参数,这里就不写内容)
System.out.println(result); // null [show1方法定义的时候没有返回值]
System.out.println("======================");
// 执行带有参数的公有成员方法
Object o = method1.invoke(obj, "666");
System.out.println(o);
}
1.4.2 通过Method对象执行指定方法
/*
参数obj:当前Method对象所属的类的对象!【实际的对象值!】
参数args:为方法对象的形参赋值(若方法对象对应的方法定义没有参数,这里可以空着不写<这里是一个可变参数>)
返回值Object:执行完当前方法的返回结果!
*/
Object invoke(Object obj, Object... args) // 在具有指定参数的指定对象上调用此 方法对象表示的基础方法。 【★★★★★】
public static void main(String[] args) throws Exception{
// 获得Student类对应的Class对象
Class<?> clazz = Class.forName("com.itheima.demo01_反射.Student");
// 创建类的对象
Object obj = clazz.getConstructor().newInstance();
// 获得指定方法(公有无参)
Method show1 = clazz.getMethod("show1");
// 获得指定方法(公有带参数)
Method method1 = clazz.getMethod("method1", String.class);
// 获得成员方法对象,期目的是为了执行这个成员方法
Object result = show1.invoke(obj);// 参数1:当前方法所属对象! 参数2:show1方法的参数值(若定义show1方法没有参数,这里就不写内容)
System.out.println(result); // null [show1方法定义的时候没有返回值]
System.out.println("======================");
// 执行带有参数的公有成员方法
Object o = method1.invoke(obj, "666");
System.out.println(o);
}
1.5 通过Class对象操作成员变量
1.5.1 获得成员变量的Field对象
public static void main(String[] args) throws Exception{
// 获得Student类对应的Class对象
Class<?> clazz = Student.class;
// 获得类的实例
//Object obj = clazz.newInstance();
Student s = (Student) clazz.newInstance();
// 获得公有成员变量对应的Field对象(很多)
Field[] fields = clazz.getFields();
System.out.println(Arrays.toString(fields));
// 获得指定公有成员变量
Field name = clazz.getField("name");
System.out.println(name);
// 获得所有的成员变量对应的Field对象(公有私有均可)
Field[] declaredFields = clazz.getDeclaredFields();
System.out.println(Arrays.toString(declaredFields));
// 获得指定成员变量(公有私有均可)
Field age = clazz.getDeclaredField("age");
System.out.println(age);
}
1.5.2 通过Field对象获取成员变量的值或者设置值
public static void main(String[] args) throws Exception{
// 获得Student类对应的Class对象
Class<?> clazz = Student.class;
// 获得类的实例
//Object obj = clazz.newInstance();
Student s = (Student) clazz.newInstance();
/操作公有的成员变量
// 获得指定公有成员变量
Field name = clazz.getField("name");
// 为公有的成员变量设置值
System.out.println(s.getName()); // null
name.set(s,"jack");
// 获得指定公有成员变量的值
System.out.println(name.get(s)); // jack
System.out.println(s.getName()); // jack
/操作私有的成员变量
// 获得指定成员变量(公有私有均可)
Field age = clazz.getDeclaredField("age");
// 暴力访问
age.setAccessible(true);
// 获取值
System.out.println(age.get(s)); // 0
// 设置值
age.set(s,38);
System.out.println(age.get(s)); // 38
}
1.6 反射开发总结
* 使用反射开发常用的固定步骤
①获得类的Class对象(有三种方式)【Class clazz】
Class.forName("全限定类名");
类名.class;
类的对象.getClass();
②创建类的实例对象(2种操作)
clazz.newInstance(); // 调用公有的无参构造方法 【不推荐使用,但是可以用!】
clazz.getDeclaredConstructor().newInstance(); // 通过构造方法对象创建对象的!
③通过clazz对象创建指定方法的Method对象或者指定属性名称的Field对象(很少操作Field)
clazz.getMethod("方法名称",方法的参数类型); // 得到Metho对象method
④使用method对象调用invoke方法,执行这个指定的方法!
Obejct obj = method.invoke(Object obj,T...parameter); // 指定这个方法是哪个对象的,给定这个方法的实际参数,得到这个方法执行的结果obj