云计算百科
云计算领域专业知识百科平台

反射......

初识反射

概述:

  • 反射是java语言提供的一种动态获取对象属性和方法的机制。

查看有Class实例的属性


  • Class class1 = Object.class;
  • 接口
    Class class2 = Comparable.class;
  • 数组
    Class class3 = String[].class;
  • 枚举
    Class class4 = Season.class;
  • 注解
    Class class5 = SuppressWarnings.class;
  • 基本数据类型
    Class class6 = int.class;
  • void
    Class class7 = void.class;

获取class实例

  • 获取class实例的方法:
    • 要求编译时期已知类型 类名称.Class
    • 获取对象的运行时类型 obj.getClass()
    • 获取编译期间未知类型 Class.forName(“包名.类名”)

Class类的常用方法

  • Class.forName 返回指定类名 name 的 Class 对象
  • Class.newInstance() 返回该Class对象所表示的类或接口的实例
  • Class.getClassLoader() 返回该Class对象所表示的类或接口的类加载器

@Test
public void test02() throws Exception {
//返回指定类名 name 的 Class 对象
Class class1 = Class.forName("com.jiazhong.L_反射的使用.test01.Person");
System.out.println(class1);
//调用缺省构造函数,返回该Class对象的一个实例
Class class2 = class1.newInstance().getClass();//就相当于 new
System.out.println(class2);
//返回该类的类加载器
ClassLoader classLoader = class1.getClassLoader();
System.out.println(classLoader);
}

  • 查看某个类的类加载器对象
    • 获取默认系统加载器 ClassLoader.getSystemClassLoader()
    • 获取某个类的类加载器对象 Class.getClassLoader()
    • 获取某个类的父类加载器对象 ClassLoader.getParent() 【如果是根加载器,则返回null】

@Test
public void test01() throws ClassNotFoundException {
//获取默认的系统加载器
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println(classLoader);
//查看某个类是哪个类加载器加载的
ClassLoader classLoader1 = Class.forName("com.jiazhong.L_反射的使用.test01.Person").getClassLoader();
System.out.println(classLoader1);
ClassLoader classLoader2 = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader2);
//获取某个类加载器的父加载器
ClassLoader classLoader3 = classLoader.getParent();
System.out.println(classLoader3);
}

反射的使用

创建运行时类的对象 newInstance

@Test
public void test01() throws Exception {
Class clazz = Person.class;
//创建Person类的实例
Person person = (Person) clazz.newInstance();
System.out.println(person);
}

获取运行时类的完整的结构

  • 获取属性
    • getFields() 获取到运行时类本身及其所有的父类中声明为public权限的属性
    • getDeclaredFields()获取当前运行时类中声明的所有属性

@Test
public void test02() {
Class clazz = Person.class;
//获取所有的属性和细节
//getFields():获取到运行时类本身及其所有的父类中声明为public权限的属性
Field[] fields = clazz.getFields();
//进行遍历
for (Field f : fields) {
System.out.println(f);
}
System.out.println("———————————————-");
//getDeclaredFields():获取当前运行时类中声明的所有属性
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f : declaredFields) {
System.out.println(f);
}
}

  • getModifiers() 获取权限修饰符
  • getType() 获取属性的类型
  • getName() 获取变量名

@Test
public void test03() {
Class clazz = Person.class;
Field[] fields = clazz.getDeclaredFields();
for (Field f : fields) {
//1.权限修饰符
int modifiers = f.getModifiers();
System.out.println(Modifier.toString(modifiers));
//2.数据类型
Class<?> type = f.getType();
System.out.println(type.getName());
//3.变量名
String name = f.getName();
System.out.println(name);
}
}

  • 获取所有的方法以及相关的细节
    • getMethods() 获取到运行时类本身及其所有的父类中声明为public权限的方法
    • getDeclaredMethods() 获取当前运行时类中声明的所有方法

public void test04() {
Class clazz = Person.class;
//getMethods():获取到运行时类本身及其所有的父类中声明为public权限的方法
Method[] methods = clazz.getMethods();
for (Method m : methods) {
System.out.println(m);
}
System.out.println("————————————————–");
//getDeclaredMethods():获取当前运行时类中声明的所有方法
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method m : declaredMethods) {
System.out.println(m);
}
}

  • 注解信息
  • getAnnotations() 获取当前运行时类中声明的所有注解
  • getModifiers() 权限修饰符
  • getReturnType() 返回值类型
  • getName() 方法名
  • parameterTypes (形参类型1 参数1,形参类型2 参数2,…)
  • getExceptionTypes() throws 异常类型1,…{}

@Test
public void test05() {
Class clazz = Person.class;
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method m : declaredMethods) {
// 1.获取方法声明的注解
Annotation[] annotations = m.getAnnotations();
for (Annotation a : annotations) {
System.out.println(a);
}
// 2.权限修饰符
System.out.println(Modifier.toString(m.getModifiers()));
// 3.返回值类型
System.out.println(m.getReturnType().getName());
// 4.方法名
System.out.println(m.getName());
// 5.形参列表
Class<?>[] parameterTypes = m.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println(parameterType.getName());
}
// 6.异常列表
Class<?>[] exceptionTypes = m.getExceptionTypes();
for (Class<?> exceptionType : exceptionTypes) {
System.out.println(exceptionType.getName());
}
}
}

  • 获取其他结构(构造器、父类、接口、包、注解等)
    • getSuperclass()获取运行时类的父类
    • getInterfaces()获取运行时类实现的接口
    • getPackage()获取运行时类所在的包
    • getGenericSuperclass()获取运行时类的带泛型的父类

@Test
public void test06() throws Exception {
//1. getSuperclass()获取运行时类的父类
Class clazz = Class.forName("com.jiazhong.L_反射的使用.test02.Person");
Class superclass = clazz.getSuperclass();
System.out.println(superclass);
//2. getInterfaces()获取运行时类实现的接口
Class[] interfaces = clazz.getInterfaces();
for (Class c : interfaces) {
System.out.println(c);
}
//3. getPackage()获取运行时类所在的包
Package aPackage = clazz.getPackage();
System.out.println(aPackage);
//4. getGenericSuperclass()获取运行时类的带泛型的父类
Type genericSuperclass = clazz.getGenericSuperclass();
System.out.println(genericSuperclass);
}

  • 获取运行时类的父类的泛型 (难)

@Test
public void test07() throws Exception {
Class clazz = Class.forName("com.jiazhong.L_反射的使用.test02.Person");
//getGenericSuperclass()获取带泛型的父类(Type是一个接口,Class实现了此接口
Type genericSuperclass = clazz.getGenericSuperclass();
//强转目前得到的泛型的类型
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
//调用getActualTypeArguments获取泛型的参数,获取到的泛型参数可能是一个数组
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
System.out.println(actualTypeArguments[1].getTypeName());
}

调用指定属性和方法

package com.jiazhong.L_反射的使用.test02;

import org.junit.jupiter.api.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectTest {
//********************如下是调用指定的属性************************
/*
* 反射的应用3-1:调用指定的属性
*
* */

//public int age = 1;
@Test
public void test01() throws Exception {
Class clazz = Person.class;
Person person = (Person) clazz.newInstance();

//获取运行时类的指定名称的属性
Field age = clazz.getField("age");
//获取或设置此属性的值
age.set(person, 30);
System.out.println(age.get(person));
}

/// /private String name;
@Test
public void test02() throws Exception {
Class clazz = Person.class;
Person person = (Person) clazz.newInstance();

//获取运行时类的指定名称的属性(私有化的属性使用)getDeclaredField来进行获取
Field name = clazz.getDeclaredField("name");
//setAccessible(true):确保此属性是可以访问的
name.setAccessible(true);
//获取或设置此属性的值
name.set(person, "Zhangsan");
System.out.println(name.get(person));
}

//private static String info;
@Test
public void test03() throws Exception {
Class clazz = Person.class;
Person person = (Person) clazz.newInstance();

//获取运行时类的指定名称的属性(私有化的属性使用)getDeclaredField来进行获取
Field info = clazz.getDeclaredField("info");
//setAccessible(true):确保此属性是可以访问的
info.setAccessible(true);
//获取或设置此属性的值
//因为是类变量,我们就可以把对象名省略掉,用null来进行表示
info.set(null, "I am a boy.");
System.out.println(info.get(person));
}

//********************如下是调用指定的方法************************
/*
* 反射的应用3-2:调用指定的方法
*
* */

//private String showNation(String nation,int age)
@Test
public void test04() throws Exception {
Class clazz = Person.class;
Person person = (Person) clazz.newInstance();

//获取运行时类的指定名称的属性(私有化的属性使用)getDeclaredMethod来进行获取
Method showNation = clazz.getDeclaredMethod("showNation", String.class, int.class);
//setAccessible(true):确保此属性是可以访问的
showNation.setAccessible(true);
//3.通过Method实例调用invoke(Object obj,Object … objs),即为对Method对应的方法的调用。
//invoke()的返回值即为Method对应的方法的返回值
//特别的:如果Method对应的方法的返回值类型为void,则invoke()返回值为null
Object invoke = showNation.invoke(person, "中国", 30);
System.out.println(invoke);
}

//public static void showInfo()
@Test
public void test05() throws Exception {
Class clazz = Person.class;
Person person = (Person) clazz.newInstance();

//获取运行时类的指定名称的属性(私有化的属性使用)getDeclaredMethod来进行获取
Method showNation = clazz.getDeclaredMethod("showInfo");
//setAccessible(true):确保此属性是可以访问的
showNation.setAccessible(true);
//3.通过Method实例调用invoke(Object obj,Object … objs),即为对Method对应的方法的调用。
//invoke()的返回值即为Method对应的方法的返回值
//特别的:如果Method对应的方法的返回值类型为void,则invoke()返回值为null
Object invoke = showNation.invoke(null);
System.out.println(invoke);
}

//********************如下是调用指定的构造器************************
/*
* 反射的应用3-3:调用指定的构造器
*
* */

//private Person(String name, int age)
@Test
public void test06() throws Exception {
Class clazz = Person.class;
//获取运行时类的指定名称的属性(私有化的属性使用)getDeclaredMethod来进行获取
Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);
//setAccessible(true):确保此属性是可以访问的
constructor.setAccessible(true);
//3.通过Constructor实例调用newInstance(Object … objs),返回一个运行时类的实例。
Person person1 = (Person) constructor.newInstance("李四", 40);
System.out.println(person1);
}

//使用Constructor替换原有的使用Class调用newInstance()的方式创建对象
@Test
public void test07() throws Exception {
Class clazz = Person.class;
//1.通过Class的实例调用getDeclaredConstructor(Class … args),获取指定参数类型的构造器
Constructor declaredConstructor = clazz.getDeclaredConstructor();
//2.设置可以进行访问
declaredConstructor.setAccessible(true);

//3.通过Constructor实例调用newInstance(Object … objs),返回一个运行时类的实例。
Person person = (Person) declaredConstructor.newInstance();
System.out.println(person);
}
}

反射的动态性

package com.jiazhong.L_反射的使用.test04;

import org.junit.jupiter.api.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Date;

public class ReflectTest {
//静态性 唯一的
public Person getInstance() {
return new Person();
}

//体会:反射的动态性
//举例1:造对象
public <T> T getInstance(String className) throws Exception {
Class clazz = Class.forName(className);
Constructor declaredConstructor = clazz.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
return (T) declaredConstructor.newInstance();
}

@Test
public void test01() throws Exception {
Person p1 = getInstance();
System.out.println(p1);

/*String className = "com.jiazhong.G_面向对象高级编程.代码块.Animal";
Person p2 = getInstance(className);
System.out.println(p2);*/

String dateName = "java.util.Date";
Date p3 = getInstance(dateName);
System.out.println(p3);
}

//体会:反射的动态性
//举例2:方法的调用
public Object invoke(String className, String methodName) throws Exception {
Class clazz = Class.forName(className);
Constructor declaredConstructor = clazz.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance();
Method declaredMethod = clazz.getDeclaredMethod(methodName);
return declaredMethod.invoke(o);
}

@Test
public void test02() throws Exception {
String className = "com.jiazhong.L_反射的使用.test04.Person";
String methodName = "show";

Object invoke = invoke(className, methodName);
System.out.println(invoke);
}
}

获取配置文件信息

  • Properties
    • Properties 类是 Java 中用于处理配置文件的一个实用工具类,它继承自 Hashtable 类,允许应用程序读取和写入那些可在运行时进行修改的配置文件。在 Java 中,配置文件通常以 .properties 格式存在,它们以键值对的形式存储数据。
  • Properties 类提供了一系列方法来操作键值对数据。最常用的方法包括:
    • getProperty(String key): 根据指定的键获取属性值。
    • setProperty(String key, String value): 设置或更新属性值。
    • load(InputStream inStream): 从输入流中读取属性列表。
    • store(OutputStream out, String comments): 将属性列表写入输出流。

加载配置文件的两种方法

  • 方式1:使用FileInputStream
    • 要求:配置文件必须放置在当前module的src目录下
    • my-project/
      ├─ module1/
      │ ├─ src/
      │ │ └─ config.properties
      │ └─ pom.xml <- 模块根目录
      └─ module2/
  • 方式2:使用类加载器
    • 从 Java 的类路径中查找资源,不依赖具体文件系统路径。
      相对路径规则:
      无前置路径(如 config.properties):默认从类路径的根目录查找。
      带前置路径(如 com/example/config.properties):从对应包路径查找。
    • my-project/
      ├─ module1/
      │ ├─ src/
      │ │ └─ config.properties
      │ └─ pom.xml <- 模块根目录
      └─ module2/

@Test
public void test02() throws IOException {
Properties properties = new Properties();
//方式1:此时默认的相对路径是当前的module
//FileInputStream in = new FileInputStream("src//config.properties");

//方式2:使用类的加载器
//此时默认的相对路径是当前module的src目录
InputStream in = ReflectionTest04.class.getClassLoader().getResourceAsStream("config.properties");

properties.load(in);
//获取配置文件中的信息
String name = properties.getProperty("username");
String password = properties.getProperty("password");
System.out.println("name = " + name + ", password = " + password);

}

反射综合习题

案例:榨汪机榨水果汁,水果分别有苹果(Apple)、香蕉(Banana)、桔子(Orange)等。
提示:
1、声明(Fruit)水果接口,包含榨汁抽象方法:void squeeze();;
2、声明榨汁机(Juicer),包含运行方法:publicvoidrun(Fruit f),方法体中,调用f的榨汁方法squeeze()
3、声明各种水果类,实现水果接口,并重写squeeze();
4、在src下,建立配置文件:config.properties,并在配置文件中配上fruitName=xxx(其中xx为某种水果的全类名
5、在FruitTest测试类中,
(1)读取配置文件,获取水果类名,并用反射创建水果对象,
(2)创建榨汁机对象,并调用run(方法

package com.jiazhong.jiaocheng.reflection.exercise;

public class Apple implements Fruit{
@Override
public void squeeze() {
System.out.println("apple汁");
}
}

package com.jiazhong.jiaocheng.reflection.exercise;

public class Banana implements Fruit{
@Override
public void squeeze() {
System.out.println("banana汁");
}
}

package com.jiazhong.jiaocheng.reflection.exercise;

public class Orange implements Fruit{
@Override
public void squeeze() {
System.out.println("orange汁");
}
}

package com.jiazhong.jiaocheng.reflection.exercise;

public interface Fruit {
void squeeze();
}

package com.jiazhong.jiaocheng.reflection.exercise;

public class Juicer {
public void run(Fruit f){
f.squeeze();
}
}

package com.jiazhong.jiaocheng.reflection.exercise;

import java.io.InputStream;

import java.lang.reflect.Constructor;
import java.util.Properties;

public class FruitTest {
public static void main(String[] args) throws Exception {
Properties properties=new Properties();// 创建Properties集合对象,为了获取配置文件
InputStream in=FruitTest.class.getClassLoader().getResourceAsStream("Juicer.properties");// 创建输入流对象
properties.load(in);// 加载输入流对象
String fruit =properties.getProperty("fruitName");// 获取配置文件中 fruitName属性的值
// System.out.println(fruit);
Class<?> fruitClass= Class.forName(fruit);
Fruit fruit1= (Fruit) fruitClass.newInstance();
Juicer juicer=new Juicer();
juicer.run(fruit1);
}
}
Properties properties=new Properties();// 创建Properties集合对象,为了获取配置文件
InputStream in=FruitTest.class.getClassLoader().getResourceAsStream("Juicer.properties");// 创建输入流对象
properties.load(in);// 加载输入流对象
String fruit =properties.getProperty("fruitName");// 获取配置文件中 fruitName属性的值
// System.out.println(fruit);
Class<?> fruitClass= Class.forName(fruit);
Fruit fruit1= (Fruit) fruitClass.newInstance();
Juicer juicer=new Juicer();
juicer.run(fruit1);
}
}

赞(0)
未经允许不得转载:网硕互联帮助中心 » 反射......
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!