反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意属性和方法;这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制!
- 反射允许对成员变量、成员方法和构造方法的信息进行编程访问
- 反射就可以把它理解成是一个人,这个人它可以把成员变量、成员方法以及构造方法, 挨个的都获取出来,并对它们进行操作!
获取出来有什么用呢?
- 当然有用了,比如IDEA里面自动提示的功能,就是利用反射来实现的。
- 我们在程序中创建了一个对象,利用这个对象去调用方法,或者是调用成员变量,那么IDEA就会利用反射把这个类里面能调用的所有的方法,所有的成员变量获取出来并进行展示!
- 还有我们在创建对象或者是调用方法的时候,方法的形参给忘了,可以按Ctrl + P加以提示,此时IDEA就是利用反射去获取这个方法上所有的形参并展示出来!
- 还有当我们在一个类里面定义了私有的构造方法,那么在测试类当中,IDEA给出的自动提示就不让去调用那个私有的构造方法,这是怎么做到的呢?
就是利用反射,利用反射获取到该类的构造方法的权限修饰符,然后一看是private的,IDEA给出的自动提示就不会提示该私有的构造方法参数!=======================================================================
问题1:说白了反射就是可以从类里面拿东西,拿什么?
- 拿成员变量、构造方法、成员方法,把它们挨个的拿出来,这就是反射!
问题2:既然都是拿东西,为什么不用IO流而要用反射呢?
- IO流是从上往下一行一行的去读,当我们读到构造方法和普通成员方法的时候,怎么区分?很难了把,如果想着用返回值区分,那么成员变量和局部变量你怎么区分呢?还得通过上下文去区分,非常的麻烦!
- 反射甚至可以利用获取出来的构造方法去创建对象!
- 反射还能够运行获取出来的方法!
- 在反射面前,一切都是赤裸裸的。
- 利用反射,我们就可以获取到类里面所有的信息!
- 反射的第一步永远都是先获取Class类对象,如此才可以解析类中的全部成分!
- 在获取的时候,不是从Java文件当中去获取的,而是从Class字节码文件当中去获取的!
- Class类就是用来描述字节码文件的!
- 同一个类编译后只产生一份class文件!
Class是一种类:
package com.gch.d2_reflect_class;
public class Student {
}
package com.gch.d2_reflect_class;
/**
目标:反射的第一步,获取class对象,也就是类对象
一个类编译后只产生一份class文件
获取class对象的三种方式:
*/
public class Test {
public static void main(String[] args) throws Exception{
// 1.调class类中的一个静态方法:forName(String className===>全限名/全类名:包名 + 类名)
// 最为常用的
Class c1 = Class.forName("com.gch.d2_reflect_class.Student");
System.out.println(c1); // class com.gch.d2_reflect_class.Student,本质就是Student.class
// 2.类名.class
// 一般更多的是当作参数进行传递(比如多线程的synchronized的同步锁对象用的就是class对象)
Class c2 = Student.class;
System.out.println(c1);// class com.gch.d2_reflect_class.Student
// 3. java.lang.Object下的一个方法,对象.getClass(),获取对象对应类的Class对象
// 当我们已经有了这个类的对象时,才可以使用
Student s = new Student();
Class c3 = s.getClass();
System.out.println(c3); // class com.gch.d2_reflect_class.Student
}
}
- 在Java当中,有一个思想叫做万物皆对象
- 比如字节码文件可以看作是Class这个类的对象
- 同理,构造方法也可以把它看作是一个对象,在Java当中定义了一个类叫做Constructor,那么这个类就是用来描述构造方法的!同理这个类的对象,就表示构造方法的对象!
- 在Java当中,有一个类叫Field,它是用来描述成员变量的,所以这个类的对象就表示成员变量的对象。
- 第三个成员,方法,在Java当中有一个类叫Method,用来描述成员方法,同理,这个类的对象就是成员方法的对象!
package com.gch.d3_reflect_constructor;
public class Student {
private String name;
private int age;
public Student() {
System.out.println("无参构造器执行!");
}
public Student(String name){
this.name = name;
}
protected Student(int age){
this.age = age;
}
private Student(String name, int age) {
System.out.println("有参构造器执行!");
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
package com.gch.d3_reflect_constructor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Parameter;
/**
目标:反射获取Constructor构造方法
*/
public class MyReflectDemo {
public static void main(String[] args) throws Exception {
/*
Class类中用于获取构造方法的方法
Constructor>[] getConstructors()
Constructor>[] getDeclaredConstructors()
Constructor getConstructor(Class>... parameterTypes)
Constructor getDeclaredConstructor(Class>... parameterTypes)
Constructor类中用于创建对象的方法
T newInstance(Object... initargs)
public void setAccessible(boolean flag)
*/
// 1.获取class字节码文件的对象
Class clazz = Class.forName("com.gch.d3_reflect_constructor.Student");
// 2.获取构造方法
// 获取所有的公共的构造方法
Constructor[] cons1 = clazz.getConstructors();
for (Constructor con : cons1) {
System.out.println(con);
}
System.out.println("====================================");
// 获取所有的构造方法
Constructor[] cons2 = clazz.getDeclaredConstructors();
for (Constructor con : cons2) {
System.out.println(con);
}
System.out.println("=====================================");
// 获取到空参的构造方法
Constructor con3 = clazz.getDeclaredConstructor();
System.out.println(con3);
System.out.println();
// 获取到指定参数的构造方法,这个参数要跟构造方法里面的参数要保持一致才是可以的
Constructor con4 = clazz.getDeclaredConstructor(String.class);
System.out.println(con4);
System.out.println();
// 在Java当中,int也是有对应字节码文件的
Constructor con5 = clazz.getDeclaredConstructor(int.class);
System.out.println(con5);
System.out.println("====================================");
// getDeclared只是让你看到这个构造,但是无法利用它直接创建对象
Constructor con6 = clazz.getDeclaredConstructor(String.class,int.class);
System.out.println(con6);
// 获取该构造方法的权限修饰符,返回值类型是int
int modifiers = con6.getModifiers();
System.out.println(modifiers); // 2
System.out.println("================================");
// con6.getParameterCount(); 获取到参数的个数
// con6.getParameterTypes(); 获取到参数的类型
// 获取到这个构造方法所有的参数
Parameter[] parameters = con6.getParameters();
for (Parameter parameter : parameters) {
System.out.println(parameter); // 表示构造方法的每一个参数
}
// 表示临时取消权限校验,如果遇到私有构造器,可以暴力反射,表示权限被打开
con6.setAccessible(true); // 暴力反射
// 利用获取到的构造方法去创建对象,要传递参数,参数的属性要跟构造方法的参数保持一致
Student stu = (Student) con6.newInstance("张三",24);
System.out.println(stu);
}
}
package com.gch.d4_reflect_field;
public class Student {
private String name; // 姓名
private int age; // 年龄
public String gender; // 性别
public Student() {
}
public Student(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
* @return gender
*/
public String getGender() {
return gender;
}
/**
* 设置
* @param gender
*/
public void setGender(String gender) {
this.gender = gender;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + ", gender = " + gender + "}";
}
}
package com.gch.d4_reflect_field;
import java.lang.reflect.Field;
public class MyReflectDemo {
public static void main(String[] args) throws Exception {
/*
Class类中用于获取成员变量的方法:
Field[] getFields() 返回所有公共成员变量对象的数组(只能拿public的)
Field[] getDeclaredFields() 返回所有成员变量对象的数组,存在就能拿到
Field getField(String name) 返回单个公共成员变量对象(只能拿public的)
Field getDeclaredField(String name) 返回单个成员变量对象,存在就能拿到
Filed类中用于创建对象的方法:
void set(Object obj,Object value):赋值
Object get(Object obj): 获取值
*/
// 1.获取class字节码文件的对象
Class clazz = Class.forName("com.gch.d4_reflect_field.Student");
// 2.获取成员变量
// 获取所有的公共的成员变量
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field); // public java.lang.String com.gch.d4_reflect_field.Student.gender
}
System.out.println();
// 获取所有的成员变量
Field[] fields2 = clazz.getDeclaredFields();
for (Field field : fields2) {
System.out.println(field); // private int com.gch.d4_reflect_field.Student.age
}
System.out.println();
// 获取单个的公共成员变量
Field gender = clazz.getField("gender");
System.out.println(gender); // public java.lang.String com.gch.d4_reflect_field.Student.gender
System.out.println();
// 获取单个的全部成员变量
Field name = clazz.getDeclaredField("name");
System.out.println(name);
// 获取权限修饰符
int modifiers = name.getModifiers();
System.out.println(modifiers); // 2
// 获取成员变量的名字
String nameStr = name.getName();
System.out.println(nameStr); // name
// 获取到成员变量的数据类型
Class nameType = name.getType();
System.out.println(nameType); // class java.lang.String
System.out.println();
// 获取成员变量记录的值
// 要获取值,首先要创建一个对象
Student s = new Student("张三",24,"男");
// 表示临时取消访问权限,暴力反射,表示权限被打开了
name.setAccessible(true);
String nameValue = (String) name.get(s);
// Object nameValue = name.get(s);
System.out.println(nameValue); // 张三
// 修改对象里面记录的值
// 参数1:修改哪个对象里面的值 参数2:修改成什么
name.set(s,"李四");
System.out.println(s); // Student{name = 李四, age = 24, gender = 男}
}
}
package com.gch.d5_reflect_method;
import java.io.IOException;
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public void sleep(){
System.out.println("睡觉");
}
private String eat(String something) throws IOException,NullPointerException,ClassCastException {
System.out.println("在吃" + something);
return "奥里给";
}
private void eat(String something,int a){
System.out.println("在吃" + something);
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
package com.gch.d5_reflect_method;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
/**
目标:利用反射获取成员方法
*/
public class MyReflectDemo {
public static void main(String[] args) throws Exception {
/*
反射获取类的Method方法对象:
1、Method getMethod(String name,Class...args);
根据方法名和参数类型获得对应的方法对象,只能获得public的
2、Method getDeclaredMethod(String name,Class...args);
根据方法名和参数类型获得对应的方法对象,包括private的
3、Method[] getMethods();
获得类中的所有成员方法对象,返回数组,只能获得public修饰的且包含父类的
4、Method[] getDeclaredMethods();
获得类中的所有成员方法对象,返回数组,只获得本类申明的方法。
Method的方法执行:
Object invoke(Object obj, Object... args)
参数一:触发的是哪个对象的方法执行。
参数二: args:调用方法时传递的实际参数
获取方法的修饰符
获取方法的名字
获取方法的形参
获取方法的返回值
获取方法的抛出的异常
*/
// 1.获取class字节码文件的对象
Class clazz = Class.forName("com.gch.d5_reflect_method.Student");
// 2.获取里面所有的方法对象
// 获取所有的公共成员方法对象(包含父类中所有的公共方法)
Method[] methods1 = clazz.getMethods();
for (Method method : methods1) {
System.out.println(method);
}
System.out.println();
// 获取里面所有的方法对象(不能获取父类的,但是可以获取本类中私有的方法)
Method[] methods2 = clazz.getDeclaredMethods();
for (Method method : methods2) {
System.out.println(method);
}
// 获取单个的公共成员方法对象
// 参数1:方法名 参数2:参数的形参
// 获取指定的单个的所有成员方法对象
Method m = clazz.getDeclaredMethod("eat", String.class);
System.out.println(m); // private void com.gch.d5_reflect_method.Student.eat(java.lang.String)
// 获取方法的修饰符
int modifiers = m.getModifiers();
System.out.println(modifiers); // 2
// 获取方法的名字
String name = m.getName();
System.out.println(name); // eat
// 获取方法的形参
// 获取到方法参数的个数
int count = m.getParameterCount();
System.out.println(count); // 1
// 获取到参数的类型
Class[] types = m.getParameterTypes();
for (Class type : types) {
System.out.println(type); // class java.lang.String
}
// 获取到参数的对象
Parameter[] parameters = m.getParameters();
for (Parameter parameter : parameters) {
System.out.println(parameter); // java.lang.String arg0
}
// 获取到方法抛出的异常
Class[] exceptionTypes = m.getExceptionTypes();
for (Class exceptionType : exceptionTypes) {
System.out.println(exceptionType); // class java.io.IOException
// class java.lang.NullPointerException
// class java.lang.ClassCastException
}
// 方法运行
/* Method的方法执行:invoke方法就可以运行你获取出来的方法
Object invoke(Object obj, Object... args):运行方法
参数一:表示方法的调用者,用obj对象调用该方法
参数二: args:方法的形参,表示在调用方法时传递的实际参数(如果没有就不写)
返回值:方法的返回值(如果没有就不写)*/
Student s = new Student();
// 表示临时取消访问权限,暴力反射,表示权限被打开
m.setAccessible(true);
// 参数一s:表示方法的调用者
// 参数二"稀饭":表示在调用方法的时候传递的实际参数
String result = (String) m.invoke(s,"稀饭"); // 在吃稀饭
// 获取到方法的返回值(要方法运行之后才能获取到方法的返回值)
System.out.println(result); // 奥里给
}
}
你觉得反射好不好?好,有两个方向
- 第一个方向:反射可以无视修饰符访问类中的内容。但是这种操作在开发中一般不用,都是框架底层来用的。
- 第二个方向:反射可以跟配置文件结合起来使用,动态的创建对象并动态的调用方法。
package com.gch.d6_reflect_test;
public class Student {
private String name;
private int age;
private char gender; // 性别
private double height;
private String hobby;
public Student() {
}
public Student(String name, int age, char gender, double height, String hobby) {
this.name = name;
this.age = age;
this.gender = gender;
this.height = height;
this.hobby = hobby;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
* @return gender
*/
public char getGender() {
return gender;
}
/**
* 设置
* @param gender
*/
public void setGender(char gender) {
this.gender = gender;
}
/**
* 获取
* @return height
*/
public double getHeight() {
return height;
}
/**
* 设置
* @param height
*/
public void setHeight(double height) {
this.height = height;
}
/**
* 获取
* @return hobby
*/
public String getHobby() {
return hobby;
}
/**
* 设置
* @param hobby
*/
public void setHobby(String hobby) {
this.hobby = hobby;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", height = " + height + ", hobby = " + hobby + "}";
}
}
package com.gch.d6_reflect_test;
public class Teacher {
private String name;
private double salary; // 工资,薪水
public Teacher() {
}
public Teacher(String name, double salary) {
this.name = name;
this.salary = salary;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return salary
*/
public double getSalary() {
return salary;
}
/**
* 设置
* @param salary
*/
public void setSalary(double salary) {
this.salary = salary;
}
public String toString() {
return "Teacher{name = " + name + ", salary = " + salary + "}";
}
}
package com.gch.d6_reflect_test;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.Writer;
import java.lang.reflect.Field;
/**
目标:对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中去
*/
public class MyReflectDemo {
public static void main(String[] args) throws Exception {
Student s = new Student("小A",23,'女',167.5,"睡觉");
Teacher t = new Teacher("播妞",10000);
saveObject(s);
System.out.println();
saveObject(t);
}
/**
* 把对象里面所有的成员变量名和值保存到本地文件当中
* @param obj
*/
public static void saveObject(Object obj) throws Exception {
// 1.获取类的字节码文件的对象:对象都有了,直接对象.getClass()即可,我真笨!
Class clazz = obj.getClass();
// 2.创建IO流
BufferedWriter bw = new BufferedWriter(new FileWriter("day13-reflect-demo\\src\\a.txt",true)); // 追加数据管道
// 3.获取到所有的成员变量对象
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
// 一律暴力破解,临时取消访问取消,表示访问权限被打开
field.setAccessible(true);
// 获取成员变量的名字
String name = field.getName();
// 获取成员变量的值
Object value = field.get(obj);
// 写出数据
bw.write(name + " = " + value);
// 换行
bw.newLine();
}
// 关闭IO流,释放资源
bw.close();
}
}
package com.gch.d7_reflect_test;
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public void study(){
System.out.println("学生在学习!");
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
package com.gch.d7_reflect_test;
public class Teacher {
private String name;
private double salary; // 薪水
public Teacher() {
}
public Teacher(String name, double salary) {
this.name = name;
this.salary = salary;
}
public void teach(){
System.out.println("老师在教书!");
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return salary
*/
public double getSalary() {
return salary;
}
/**
* 设置
* @param salary
*/
public void setSalary(double salary) {
this.salary = salary;
}
public String toString() {
return "Teacher{name = " + name + ", salary = " + salary + "}";
}
}
package com.gch.d7_reflect_test;
import java.io.FileReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;
/**
目标:反射可以跟配置文件结合的方式,动态的创建对象,并调用方法
*/
public class MyReflectDemo {
public static void main(String[] args) throws Exception {
// 1.读取配置文件中的信息
Properties prop = new Properties();
// 创建字节输入流用于读取文件
FileReader fr = new FileReader("day13-reflect-demo\\prop.properties");
// 加载属性文件的键值对信息到属性集合prop中去
prop.load(fr);
// 释放资源
fr.close();
System.out.println(prop); // {classname="com.gch.d7_reflect_test.Teacher", method=teach}
// 2.获取全类名和方法名
String className = (String) prop.get("classname");
String methodName = (String) prop.get("method");
System.out.println(className); // "com.gch.d7_reflect_test.Teacher"
System.out.println(methodName); // teach
// 3.利用反射去创建对象并运行方法
// 获取字节码文件的对象
System.out.println(className);
Class clazz = Class.forName(className);
System.out.println(className);
// 获取构造方法
Constructor con = clazz.getDeclaredConstructor();
con.setAccessible(true);
Object o = con.newInstance();
System.out.println(o);
// 获取成员方法并运行
Method method = clazz.getDeclaredMethod(methodName);
method.setAccessible(true); // 暴力反射,表示权限被打开
// 运行方法
method.invoke(o);
}
}
package com.gch.d8_reflect_genericity;
import java.lang.reflect.Method;
import java.util.ArrayList;
/**
目标:反射的作用-绕过编译阶段为集合添加数据
反射实现泛型擦除后,加入其他类型的元素
*/
public class ReflectDemo {
public static void main(String[] args) throws Exception{
ArrayList lists1 = new ArrayList<>();
ArrayList lists2 = new ArrayList<>();
System.out.println(lists1.getClass()); // class java.util.ArrayList
System.out.println(lists2.getClass()); // class java.util.ArrayList
// 结论:同一个类编译后只产生一份class文件
System.out.println(lists1.getClass() == lists2.getClass()); // true , 本质ArrayList.class
System.out.println("==================================================================");
ArrayList lists3 = new ArrayList<>();
lists3.add(23);
lists3.add(22);
// lists3.add("黑马"); 直接报错
// 获取字节码文件对象
Class clazz = lists3.getClass(); // ArrayList.class ===> public void add(E e)
// 获取add方法
Method method = clazz.getDeclaredMethod("add",Object.class);
// 运行获取到的add方法
boolean rs = (boolean) method.invoke(lists3,"黑马");
System.out.println(rs); // true
System.out.println(lists3); // [23, 22, 黑马]
System.out.println();
System.out.println("==================突破泛型的限制还可以这样做============================");
// 把lists3赋值给同类型的变量lists4
ArrayList lists4 = lists3;
// 给lists4集合添加元素
lists4.add("白马");
lists4.add(true);
// 输出lists3集合
System.out.println(lists3); // [23, 22, 黑马, 白马, true]
}
}