反射
- 框架 : 半成品软件
- 它需要我们对他进行, 进一步的丰富
- 我们也需要它现有的框架体系
-
反射机制
是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意属性和方法;
这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。
大白话的理解 : 反射其实就是对类的一种解刨
反射操作的是字节码对象
字节码文件–Class类–字节码对象
获取Class类对象的三种方式
-
三种方式分类
-
Class.forName(全类名)方法
//1.Class类中的静态方法forName("全类名") //全类名:包名 + 类名 Class clazz = Class.forName("com.itheima.myreflect2.Student");
-
类名.class属性
//2.通过class属性来获取 Class clazz2 = Student.class;
-
对象名.getClass()方法
//3.利用对象的getClass方法来获取class对象 //getClass方法是定义在Object类中. Student s = new Student(); Class clazz3 = s.getClass(); } }
-
反射获取构造方法并使用
Class类获取构造方法对象的方法
方法介绍
方法名 | 说明 |
---|---|
Constructor<?>[] getConstructors() | 返回所有公共构造方法对象的数组 |
Constructor<?>[] getDeclaredConstructors() | 返回所有构造方法对象的数组 |
Constructor getConstructor(Class<?>… parameterTypes) | 返回单个公共构造方法对象 |
Constructor getDeclaredConstructor(Class<?>… parameterTypes) | 返回单个构造方法对象 |
public static void main(String[] args) throws Exception {
Class<?> aClass = Class.forName("com.Student");
//反射空参构造方法(解剖)
Constructor<?> constructor = aClass.getConstructor();
//使用空参构造方法对象,构造对象
Object o = constructor.newInstance();
//反射带参构造方法(解剖)
//小括号中,一定要跟构造方法的形参保持一致
Constructor<?> constructor1 = aClass.getConstructor(String.class);
//使用有参构造方法对象,构造对象
Object o2 = constructor1.newInstance("张三");
//[暴力反射]带参构造方法(解剖)
Constructor<?> constructor2 = aClass.getDeclaredConstructor(String.class);
//开启权限
constructor2.setAccessible(true);
//使用有参构造方法对象,构造对象
Object o3 = constructor2.newInstance("王飞杨");
}
}
Constructor类用于创建对象的方法
方法介绍
方法名 | 说明 |
---|---|
T newInstance(Object…initargs) | 根据指定的构造方法创建对象 |
setAccessible(boolean flag) | 设置为true,表示取消访问检查 |
private static void method4() throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
//获取一个私有的构造方法并创建对象
//1.获取class对象
Class clazz = Class.forName("com.itheima.myreflect3.Student");
//2.获取一个私有化的构造方法.
Constructor constructor = clazz.getDeclaredConstructor(String.class);
//被private修饰的成员,不能直接使用的
//如果用反射强行获取并使用,需要临时取消访问检查
constructor.setAccessible(true);
//3.直接创建对象
Student student = (Student) constructor.newInstance("zhangsan");
System.out.println(student);
}
private static void method3() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//简写格式
//1.获取class对象
Class clazz = Class.forName("com.itheima.myreflect3.Student");
//2.在Class类中,有一个newInstance方法,可以利用空参直接创建一个对象
Student student = (Student) clazz.newInstance();//这个方法现在已经过时了,了解一下
System.out.println(student);
}
private static void method2() throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
//1.获取class对象
Class clazz = Class.forName("com.itheima.myreflect3.Student");
//2.获取构造方法对象
Constructor constructor = clazz.getConstructor();
//3.利用空参来创建Student的对象
Student student = (Student) constructor.newInstance();
System.out.println(student);
}
private static void method1() throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
//1.获取class对象
Class clazz = Class.forName("com.itheima.myreflect3.Student");
//2.获取构造方法对象
Constructor constructor = clazz.getConstructor(String.class, int.class);
//3.利用newInstance创建Student的对象
Student student = (Student) constructor.newInstance("zhangsan", 23);
System.out.println(student);
}
小结
-
获取class对象
三种方式: Class.forName(“全类名”), 类名.class, 对象名.getClass()
-
获取里面的构造方法对象
getConstructor (Class<?>... parameterTypes) getDeclaredConstructor (Class<?>… parameterTypes)
-
如果是public的,直接创建对象
newInstance(Object… initargs)
-
如果是非public的,需要临时取消检查,然后再创建对象
setAccessible(boolean) 暴力反射
反射获取成员变量并使用
- Class类获取成员变量对象的方法
方法分类
方法名 | 说明 |
---|---|
Field[] getFields() | 返回所有公共成员变量对象的数组 |
Field[] getDeclaredFields() | 返回所有成员变量对象的数组 |
Field getField(String name) | 返回单个公共成员变量对象 |
Field getDeclaredField(String name) | 返回单个成员变量对象 |
Method类用于执行方法的方法
方法介绍
方法名 | 说明 |
---|---|
Object invoke(Object obj, Object… args) | 运行方法 |
参数一: 用obj对象调用该方法
参数二: 调用方法的传递的参数(如果没有就不写)
返回值: 方法的返回值(如果没有就不写)
案例解析
public class ReflectDemo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
// Object invoke(Object obj, Object... args):运行方法
// 参数一:用obj对象调用该方法
// 参数二:调用方法的传递的参数(如果没有就不写)
// 返回值:方法的返回值(如果没有就不写)
//1.获取class对象
Class clazz = Class.forName("com.itheima.myreflect5.Student");
//2.获取里面的Method对象 function4
Method method = clazz.getMethod("function4", String.class);
//3.运行function4方法就可以了
//3.1创建一个Student对象,当做方法的调用者
Student student = (Student) clazz.newInstance();
//3.2运行方法
Object result = method.invoke(student, "zhangsan");
//4.打印一下返回值
System.out.println(result);
}
}
/*
需求: 请向一个泛型为 Integer 的集合, 添加一个 String 字符串
普及: Java中的泛型是假的, 只在编译期间有效, 运行的时候, 就没有泛型了.
运行的时候: 肯定过了编译阶段, 肯定有字节码对象.
结论 : Java中的泛型, 到运行期间, 就会被擦除掉.
*/
public static void main(String[] args) throws Exception {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(4);
list.add(3);
//获取字节码对象
Class<? extends ArrayList> aClass = list.getClass();
//反射内部成员方法
Method add = aClass.getMethod("add", Object.class);
//运行成员方法
add.invoke(list,"abc");
System.out.println(list);
}
综合案例
需求:1,编写三个任意的Javabean类,Student类,Teacher类,Worker类。
属性均为:name,age
方法要求:
Student里面写学习study和吃饭eat的方法(方法无参无返回)
Teacher里面写上课teach和吃饭eat的方法(方法无参无返回)
Worker里面写工作work和睡觉sleep的方法(方法无参无返回)
2,本地新建配置文件properties
属性:classname=Student类的全类名
methodname=study
3,在测试类中读取properties文件中的两个属性值
4,利用反射创建对象学生类的对象,并调用方法
5,修改配置文件,不修改代码,运行老师类中的上课方法
6,修改配置文件,不修改代码,运行工人类中的工作方法
public static void main(String[] args) throws Exception {
Properties prop = new Properties();
//获取配置文件的输入流
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("studentClass.properties");
//加载配置文件的键值对到集合
prop.load(is);
is.close();
//根据建获取值
String className = prop.getProperty("classname");
String methodname = prop.getProperty("methodname");
//获取类的字节码对象
Class<?> aClass = Class.forName(className);
//反射内部的构造方法
Constructor<?> constructor = aClass.getConstructor();
Object o = constructor.newInstance();
//反射内部的成员方法
Method method = aClass.getMethod(methodname);
//调用方法
method.invoke(o);
}
小结
通过字节码对象反射内部成员
- 构造方法:Constructor类(创建对象)
- 成员变量:Field类(设置\获取)
- 成员方法:Method类(调用)
方法引用
介绍 :
- 方法引用是 JDK8 开始出现的, 主要的作用, 是对Lambda表达式进行进一步的简化
格式 :
方法引用使用一对冒号 ::
方法引用通过方法的名字来指向一个方法。
方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
- 引用前 :
public class A {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"a","b","c","d");
list.forEach(s -> System.out.println(s));
}
}
- 引用后 :
public class A {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "a", "b", "c", "d");
list.forEach(System.out::println);
}
}
咋回事 ??? !!!
- 引用静态方法 :
public class A {
/*
所谓方法引用, 其实就是方法的调用
之前: A.method();
现在: A::method();
就是一个格式的转变而已
------------------------------------------
参数呢?
根据可推导, 可省略原则, 参数只有一个 s
method方法, 也恰好只要一个 s
那方法调用起来, 这一个参数, 只可能进入 method 方法中.
*/
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "a", "b", "c", "d");
list.forEach(s -> A.method(s));
list.forEach(A::method);
}
public static void method(String s) {
System.out.println(s.toUpperCase());
}
}
- 引用普通成员方法 :
1. 创建对象
2. 对象名::方法名
public class A {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "a", "b", "c", "d");
list.forEach(s -> System.out.println(s));
A a = new A();
list.forEach(a::method);
}
public void method(String s) {
System.out.println(s.toUpperCase());
}
}