反射
问题的提出
-
需求
- 根据配置文件re.properties指定信息,创建Cat对象并调用方法hi
- classfullpath=com.hspedu.Cat
- method=hi
-
Cat.java
package com.hspedu;
public class Cat {
public void hi(){
System.out.println("喵喵喵~");
}
}
- re.properties
classFullPath=com.hspedu.Cat
method=hi
- ReflectionQuestion.java
public class ReflectionQuestion {
public static void main(String[] args) {
//传统的方式创建Cat
Cat cat = new Cat();
cat.hi();
//使用properties配置文件创建类,不适用反射则无法创建类
}
}
初识反射
public class ReflectionQuestion {
public static void main(String[] args) throws Exception {
//传统的方式创建Cat
Cat cat = new Cat();
cat.hi();
//使用properties配置文件创建类,不适用反射则无法创建类
//---------------使用反射机制解决------------------
Properties properties = new Properties();
properties.load(new FileInputStream("src/main/resources/re.properties"));
//获取配置文件中的类路径和方法
String classFullPath = properties.get("classFullPath").toString();
String methodName = properties.get("method").toString();
//1.加载类
Class aClass = Class.forName(classFullPath);
//2.通过aClass得到你加载的类 com.hspedu.Cat
Object o = aClass.newInstance();
//3.通过aClass得到加载类com.hspedu.Cat的methodName(hi)方法。在反射中可以把方法当做对象
Method method = aClass.getMethod(methodName);
//通过method调用方法,即通过方法对象来实现调用方法
method.invoke(o); //输出 喵喵喵~
}
}
- 在不修改源码的情况下来控制程序(修改配置),新建cry方法。修改配置文件method=cry则直接调用cry方法不需要修改ReflectionQuestion的源码
public class Cat {
public void hi(){
System.out.println("喵喵喵~");
}
public void cry(){
System.out.println("wuwuwu~");
}
}
反射机制
- Java Reflection
- 反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息(如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到
- 加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构,这个对象就向一面镜子,透过这个镜子看到类的结构,所以,形象的称之为反射
- Java反射机制可以完成
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时得到任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 生成动态代理
- 反射相关的主要类
- java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象
- java.lang.reflect。Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Constructor:代表类的构造方法
- Cat.java
public class Cat {
public String name = "小猫";
public Cat(){
}
public Cat(String name){
this.name = name;
}
public void hi(){
System.out.println("喵喵喵~");
}
public void cry(){
System.out.println("wuwuwu~");
}
}
public class Reflection01 {
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
properties.load(new FileInputStream("src/main/resources/re.properties"));
//获取配置文件中的类路径和方法
String classFullPath = properties.get("classFullPath").toString();
String methodName = properties.get("method").toString();
//加载类
Class aClass = Class.forName(classFullPath);
//通过aClass得到你加载的类 com.hspedu.Cat
Object o = aClass.newInstance();
//通过aClass得到加载类com.hspedu.Cat的methodName(hi)方法。在反射中可以把方法当做对象
Method method = aClass.getMethod(methodName);
//通过method调用方法,即通过方法对象来实现调用方法
method.invoke(o); //输出 喵喵喵~
//得到name字段,不能获取私有的属性
Field nameField = aClass.getField("name");
// 传统写法:对象.成员变量 反射: 成员变量.get(对象)
System.out.println(nameField.get(o)); //输出 小猫
}
}
- 反射优点和缺点
- 优点:可以动态的创建爱你和使用对象(也是框架底层核心),使用灵活
- 缺点:使用反射基本是解释执行,对执行速度有影响
Class类
- Class类常用方法
- Car.java ```java package com.hspedu.class_;
public class Car {
public String brand = "宝马"; //品牌
public int price = 500000; //
public String color = "白色";
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", price=" + price +
", color='" + color + '\'' +
'}';
}
}
- Class01.java
```java
public class Class01 {
public static void main(String[] args) throws Exception {
String classPath = "com.hspedu.class_.Car";
//获取到Car类对应的Class对象
Class aClass = Class.forName(classPath);
//得到包名
String packageName = aClass.getPackage().getName();
//得到全类名
String className = aClass.getName();
//创建对象实例
Car car = (Car) aClass.newInstance();
//获取属性
Field field = aClass.getField("brand"); //字段为私有属性的话会报错(NoSuchFieldException)
String brand = (String)field.get(car);
//给属性赋值
field.set(car,"奔驰");
//遍历获取所有的属性
Field[] fields = aClass.getFields();
for (Field f : fields){
//输出每个字段的值
System.out.println(f.getName() + ":"+f.get(car));
}
}
}
- 获取Class类对象方式
public class Class02 {
public static void main(String[] args) throws Exception {
//1. 通过类的全类名
Class<?> aClass = Class.forName("com.hspedu.class_.Car");
//2. 类名.class
Class<Car> carClass = Car.class;
//3. 对象.getClass
Car car = new Car();
Class carClass1 = car.getClass();
//4. 通过类加载器获取类的Class对象
ClassLoader classLoader = car.getClass().getClassLoader();
Class<?> aClass1 = classLoader.loadClass("com.hspedu.class_.Car");
//5. 基本数据类型 .class
Class<Integer> intClass = int.class;
//6. 基本数据类型对应的包装类,可以通过 .type得到Class类对象
Class<Integer> IntegerClass = Integer.TYPE;
}
}
- 通过反射获取类的结构信息
- getName:获取全类名
- getSimpleName:获取简单类名
- getFields:获取所有public修饰的属性,包含本类以及父类的
- getMethods:获取所有public修饰的方法,包含本类以及父类
- getDeclaredMethod:获取本类中所有方法
- getConstructors:获取所有public修饰的构造器
- getDeclaredConstructors:获取本类的构造器
- getPackage:以Package形式返回包信息
- getSuperClass:以Class形式返回父类信息
- getInterface:以Class[]形式返回接口信息
- getAnnotations:以Annotation[]形式返回注解信息
- java.lang.reflect.Field类
- getModifiers:以int形式返回修饰符
- 默认修饰符0,public 1,private 2,protect 4,static 8,final 16
- getType:以Class形式返回类型
- getName:返回属性名
- getModifiers:以int形式返回修饰符
- java.lang.reflect.Method类
- getModifiers:以int形式返回修饰符
- 默认修饰符0,public 1,private 2,protect 4,static 8,final 16
- getReturnType:以Class形式获取返回类型
- getName:返回方法名
- getParameterTypes:以Class[]返回参数类型数组
- getModifiers:以int形式返回修饰符
- java.lang.reflect.Constructor类
- getModifiers:以int形式返回修饰符
- getName:返回构造器名(全类名)
- getParameterType:以Class[]返回参数类型数组
类加载
- 反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载
- 静态加载:编译时加载相关的类,如果没有则报错,依赖性太强
- 动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性
- 类加载时机
- 当创建对象时
- 当子类被加载时
- 调用类中的静态成员时
- 通过反射