本文已参与「新人创作礼」活动,一起开启掘金创作之路。
1、基本概念
java的动态处理技术,在运行时进行。
2、反射的例子
public class Student {
public String name;
public int age;
public String gender;
public Student() {
}
public Student(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
'}';
}
}
@Test
public void test1(){ //软编码,灵活,把问题延迟到运行时解决
try {
//干预类的加载,直接获取类模板对象
Class clazz = Class.forName("com.hike.javase.reflect.Student");
//通过类模板对象.newInstance创建实体对象
Object object = clazz.newInstance(); //调用无参构造器创建对象
System.out.println(object);
} catch (ClassNotFoundException e) { // 在运行时动态加载类时, 发现没有找到类
e.printStackTrace();
} catch (IllegalAccessException e) { // 非法访问, 访问权限不够时出现
e.printStackTrace();
} catch (InstantiationException e) { // 在创建对象时出现异常, 可能是构造器不存在
e.printStackTrace();
}
}
@Test
public void test(){ //编译时必须依赖类,硬编码
Student s1 = new Student(); //强烈依赖类
s1.name = "张三";
s1.age = 15;
s1.gender = "男";
System.out.println(s1.getName());
System.out.println(s1.getAge());
System.out.println(s1.getGender());
Student s2 = new Student("小红", 16, "女");
System.out.println(s2);
}
}
3、反射中关于属性的访问
public void test2(){
try {
Class clazz = Class.forName("com.hike.javase.reflect.Student");
Object object = clazz.newInstance();
System.out.println(object);
//想要使用属性,先获取属性定义的对象
//Field ageField = clazz.getField("age"); //只能获取公共的属性,不可以获取私有属性
Field ageField = clazz.getDeclaredField("age"); //获取类中的任意属性
//暴力反射,不推荐使用
ageField.setAccessible(true); //设置此属性为可访问的
//根据属性名获取属性的定义对象
ageField.set(object,15); //相当于object.age = 15;
System.out.println(ageField.get(object)); //相当于 System.out.println(object.age)
//Field nameField = clazz.getField("name");
//nameField.set(object,"小李");
//System.out.println(nameField.get(object));
System.out.println(object);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e){
e.printStackTrace();
}
}
4、一些方法
@Test
public void test5() throws ClassNotFoundException {
Class clazz = Class.forName("com.hike.javase.reflect.Student");
String name = clazz.getName();
System.out.println("类名 : " + name);
System.out.println("简单类名 : " + clazz.getSimpleName());
Class superclass = clazz.getSuperclass();
System.out.println("父类 : " + superclass);
Class[] interfaces = clazz.getInterfaces();
System.out.println("接口列表 ");
for (Class class1 : interfaces) {
System.out.println(class1);
}
}
5、获取类模板对象的几种方法
@Test
public void test6() throws ClassNotFoundException {
// 获取类模板对象的方法 ,有4种
// 1) 直接通过类.class, 效率最高, 最安全.
Class clazz1 = Student.class;
// 2) 根据对象, 调用它的getClass()方法获取, 此方法也很常用.
Student student = new Student("佟刚", 40, "男");
Class clazz2 = student.getClass();
System.out.println(clazz1 == clazz2);
// 3) 反射中最常用的 Class.forName("全限定类名");
Class clazz3 = Class.forName("com.hike.javase.reflect.Student");
System.out.println(clazz2 == clazz3);
// 4) 通过类加载器对象动态加载类
ClassLoader classLoader = this.getClass().getClassLoader();
Class clazz4 = classLoader.loadClass("com.hike.javase.reflect.Student");
System.out.println(clazz3 == clazz4);
}
@Test
public void test7() {
Class strClazz = String.class;
System.out.println(strClazz);
// 基本数据类型的类模板只能用第一种方式获取.
Class clazz1 = int.class; // 基本数据类型也有相应的类模板对象, 但是不能获取属性和方法, 只能作为一个标记来使用.
Class clazz2 = Integer.class; // 这是一个普通类模板.
System.out.println(clazz1 == clazz2);
// 判断类模板类型
System.out.println("是否是基本型 : " + clazz1.isPrimitive());
System.out.println("是否是基本型 : " + clazz2.isPrimitive());
}
6、类加载器
@Test
public void test8() {
ClassLoader classLoader1 = ClassLoader.getSystemClassLoader(); // 获取系统类加载器
System.out.println(classLoader1);
ClassLoader classLoader2 = this.getClass().getClassLoader(); // 使用最多的, 获取当前类的类加载器
System.out.println(classLoader2);
ClassLoader classLoader3 = classLoader1.getParent(); // 获取父 "类加载器", 是 扩展 "类加载器"
System.out.println(classLoader3);
ClassLoader classLoader4 = classLoader3.getParent(); // 获取到的是引导类加载器(Bootstrap)
System.out.println(classLoader4); // 这个类加载器无法获取, 无法使用
/*
双亲委派机制
用户类加载器加载类时, 必须把此加载请求转发给父类加载器, 父类加载器再继续向父类加载器委派, 直到Bootstrap类加载器
从Bootstrap类加载器开始真正加载, 各司其职.
*/
}
重要应用: 使用类加载器读取资源文件
@Test
public void test9() throws IOException {
//FileInputStream fis = new FileInputStream("只能读当前目录下的文件");
ClassLoader classLoader = this.getClass().getClassLoader();
// 只能加载build-path和src下的文件
//InputStream inputStream = classLoader.getResourceAsStream("com/sun/corba/se/impl/logging/LogStrings.properties"); // 读取资源文件, 只要是Build-Path(classpath)中的文件都可以
InputStream inputStream = classLoader.getResourceAsStream("test.properties");
Properties properties = new Properties();
properties.load(inputStream);
Set<Entry<Object, Object>> entrySet = properties.entrySet();
for (Entry<Object, Object> entry : entrySet) {
System.out.println(entry);
}
}
使用全参构造器实例化对象
@Test
public void test10() {
try {
Class clazz = Class.forName("com.hike.javase.reflect.Student");
//Object object = clazz.newInstance(); 没有无参构造器时出问题
//public Student(String name, int age, String gender) 要想定位这个构造器, 必须让参数列表一致.
// 提供形式参数类型列表, 是类模板对象的列表
Constructor constructor = clazz.getConstructor(String.class, int.class, String.class); // 定位合适的构造器
// 调用时必须给定实参列表
Object object = constructor.newInstance("程程", 20, "女"); // new Teacher("程程", 20, "女");
System.out.println(object);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) { // 参数列表出错, 或者方法名出错
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) { // 方法调用时实参和形参不匹配
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
7、反射实现方法的调用
在Student类中添加方法
public void study(String content, int time){
System.out.println("学生在上[" + content + "]课, 共上了[" + time +"]小时");
//return true;
//throw new RuntimeException("一个异常");
}
@Test
public void test11() {
try {
Class clazz = Class.forName("com.hike.javase.reflect.Student");
Constructor constructor = clazz.getConstructor(String.class, int.class, String.class);
Object object = constructor.newInstance("小明", 20, "女");
//object.study("JavaWEB", 5);
//先获取方法
Method studyMethod = clazz.getMethod("study", String.class, int.class); // 后面是方法参数类型列表
Short n = 5;
Object retValue = studyMethod.invoke(object, "JavaWEB", n); // 后面是实参列表, 如果方法没有返回值, 它的返回值是null
System.out.println(retValue);
} catch (ClassNotFoundException e) { // 类没有找到
e.printStackTrace();
} catch (NoSuchMethodException e) { // 方法没有找到, 方法名错误或参数列表错误
e.printStackTrace();
} catch (SecurityException e) { // 安全异常
e.printStackTrace();
} catch (InstantiationException e) { // 创建对象时出现异常
e.printStackTrace();
} catch (IllegalAccessException e) { // 非法访问异常
e.printStackTrace();
} catch (IllegalArgumentException e) { // 非法实参异常, 实参和形参不匹配, 类型和顺序和数量
e.printStackTrace();
} catch (InvocationTargetException e) { // 调用的目标方法内部出现异常了.
e.printStackTrace();
}
}
当调用的方法为private时,使用以下方法进行调用。
// getMethod只能获取公共的方法, 包括从父类继承的.
//Method studyMethod = clazz.getMethod("study", String.class, int.class);
// getDeclaredMethod可以获取本类中所有声明的方法
Method studyMethod = clazz.getDeclaredMethod("study", String.class, int.class);
studyMethod.setAccessible(true);
通过反射访问静态方法
Object retValue = studyMethod.invoke(null, "JavaWEB", n); // 静态方法传null, 不需要传入对象
8、通过反射获取类模板的一些内容
@Test
public void test14() throws Exception {
Class clazz = Class.forName("com.hike.javase.reflect.Student");
System.out.println("父类 : " + clazz.getSuperclass());
Constructor[] constructors = clazz.getConstructors();
for (int i = 0; i < constructors.length; i++) {
System.out.println("构造器 : " + constructors[i]);
}
System.out.println("************************************************");
Field[] fields = clazz.getFields(); // 所有公共属性
for (int i = 0; i < fields.length; i++) {
System.out.println(fields[i]);
}
Field[] declaredFields = clazz.getDeclaredFields(); // 所有本类属性
for (Field field : declaredFields) {
System.out.println(field);
}
System.out.println("************************************************");
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("************************************************");
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method method : declaredMethods) {
System.out.println(method);
}
}
熟悉运行机制,思路,在非高级编程中应用不多。