反射...

本文详细介绍了Java反射机制,包括其作用、主要API、使用场景和对比,以及如何通过反射创建对象、访问属性和方法。通过示例展示了如何获取和调用类的构造器、方法、属性等信息,并探讨了类加载器和配置文件的读取。此外,还涵盖了反射在动态代理和注解处理中的应用。
摘要由CSDN通过智能技术生成

反射概述 Java Reflection

  • Reflection是被视为动态语言的关键, 反射机制允许程序在执行期借助于Refiection API取得任何类的内部信息, 并能直接操作任意对象的内部属性及方法
  • 加载完类之后, 在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象), 这个对象就包含了完整的类的结构信息, 我们可以通过对象看到类的结构. 这个对象就像一面镜子, 透过这个镜子看到类的结构, 所以 我们形象地称之为: 反射
  • Java反射机制提供的功能
    • 在运行时判断任意一个对象所属的类
    • 在运行时构造任意一个类的对象
    • 在运行时判断任意一个类所具有的成员变量和方法
    • 在运行时获取泛型信息
    • 在运行时调用任意一个对象的成员变量和方法
    • 在运行时处理注解
    • 在运行时动态代理
  • 反射相关的主要API
    • java.lang.Class 代表一个类
    • java.lang.reflect.Method 代表类的方法
    • java.lang.reflect.Field 代表类的成员变量
    • java.lang.reflect.Constructor 代表类的构造器
  • 哪些类型可以有Class对象
    • class 外部类, 成员, 局部内部类, 匿名内部类
    • interface 接口
    • [] 数组
    • enum 枚举
    • annotation 注解@interface
    • primitive type 基本数据类型
    • void

使用反射前后对比

  • 使用反射前
@Test
public void test1(){
    Person p1 = new Person("Tom",12);//1.创建Person类的对象
    p1.age = 10;//2.通过对象,调用其内部的属性和方法
    System.out.println(p1.toString());//Person{name='Tom', age='10'}
    p1.show();//你好,我是你男朋友
}
  1. 创建Person类对象
  2. 通过对象调用其内部属性和方法

在Person类的外部,不可以通过Person类的对象调用其内部私有结构, 比如:name, showNation() 以及私有的构造器

  • 使用反射后
@Test
public void test2() throws Exception {
    Class clazz = Person.class;
    //1.通过反射,创建Person类的对象
    Constructor cons = clazz.getConstructor(String.class, int.class);
    Object obj = cons.newInstance("Tom", 12);
    Person p = (Person) obj;
    System.out.println(obj.toString());//Person{name='Tom', age='12'}
    System.out.println(p.toString());//Person{name='Tom', age='12'}
    //2.通过反射,调用对象指定的属性和方法
    //调用属性
    Field age = clazz.getDeclaredField("age");
    age.set(p, 10);
    System.out.println(p.toString());//Person{name='Tom', age='10'}
    //调用方法
    Method show = clazz.getDeclaredMethod("show");
    show.invoke(p);//你好,我是你男朋友
    //通过反射,可以调用Person类的私有结构,私有构造器,方法,属性
    //调用私有的构造器
    Constructor cons1 = clazz.getDeclaredConstructor(String.class);
    cons1.setAccessible(true);
    Person p1 = (Person) cons1.newInstance("Jerry");
    System.out.println(p1);//Person{name='Jerry', age='0'}
    //调用私有的属性
    Field name = clazz.getDeclaredField("name");
    name.setAccessible(true);
    name.set(p1,"韩梅梅");
    System.out.println(p1.toString());//Person{name='韩梅梅', age='0'}
    //调用私有的方法
    Method showNation = clazz.getDeclaredMethod("showNation", String.class);
    showNation.setAccessible(true);
    String obj1 = (String) showNation.invoke(p1, "中国");//我是中国人
    System.out.println(obj1);//中国
}

获取Class实例的四种方式

  • 方式一: 调用运行时类的属性: .class
Class<Person> clazz1 = Person.class;

可以写上泛型, 例如这里写了Person

  • 方式二: 通过运行时类的对象
Person p1 = new Person();
Class clazz2 = p1.getClass();
  • 方式三: 通用Class的静态方法: forName
Class clazz3 = Class.forName("com.atguigu.java.Person");
  • 方式四: 使用类的加载器
ClassLoader cl = A_ReflectionTest.class.getClassLoader();
Class clazz4 = cl.loadClass("com.atguigu.java.Person");

[比较] 都相等

System.out.println(clazz1 == clazz2);//true
System.out.println(clazz1 == clazz3);//true
System.out.println(clazz1 == clazz4);//true

类加载器ClassLoader

  • 对于自定义类, 使用系统类加载器进行加载

    ClassLoader classLoader = B_ClassLoaderTest.class.getClassLoader();
    System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
    
  • 调用系统类加载器的getParent()获取扩展类加载器

    ClassLoader classLoader1 = classLoader.getParent();
    System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@28a418fc
    
  • 调用扩展类加载器的getParent()无法获取引导类加载器, 引导类加载器主要负责加载java的核心类库,无法加载自定义类的

    ClassLoader classLoader2 = classLoader1.getParent();
    System.out.println(classLoader2);//null
    

读取配置文件的两种情况

  • 情况一: 配置文件在工程下
Properties pro = new Properties();
FileInputStream fis = new FileInputStream("JDBC.properties");
//如果在src下需要加src//
//FileInputStream fis1 = new FileInputStream("src//JDBC.properties");
pro.load(fis);
String user = pro.getProperty("user");
String pass = pro.getProperty("pass");
System.out.println(user+"---"+pass);//嘿咻---abc
  • 情况二: 配置文件在src下
Properties pro = new Properties();
ClassLoader classLoader = B_ClassLoaderTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("JDBC1.properties");
pro.load(is);
String user = pro.getProperty("user");
String pass = pro.getProperty("pass");
System.out.println(user+"---"+pass);//牧风---aaa

newInstance通过反射创建对应的运行时类的对象

  • 煎蛋举栗
Class clazz = Person.class;
Person o = (Person) clazz.newInstance();
System.out.println(o);//Person{name='null', age='0'}
  • 举栗子, 体会反射的动态性
@Test
public void test2() throws Exception {
    int num = new Random().nextInt(3);
    String classPath = "";
    switch (num){
        case 0:
            classPath = "java.util.Date";
            break;
        case 1:
            classPath = "java.lang.Object";
            break;
        case 2:
            classPath = "com.atguigu.java.Person";
            break;
    }
    Object obj = getInstance(classPath);
    System.out.println(obj);//Thu May 27 18:24:10 CST 2021
}
public Object getInstance(String classPath) throws Exception {
    //创建一个指定类的对象
    //classPath:指定类的全类名
    Class clazz = Class.forName(classPath);//先获取类名
    return clazz.newInstance();//类名.newInstance
}

获取

  • 获取属性结构 getFields()获取当前运行时类及其父类中声明为public访问权限的属性
Class clazz = Person.class;
Field[] fields = clazz.getFields();
for (Field f : fields){
    System.out.println(f);
    //public int com.atguigu.java1.Person.id
    //public double com.atguigu.java1.Creature.weight
    //出来的时候只出子类和父类的public
}
  • 获取属性结构 getDeclaredFields()获取当前运行时类中声明的所有属性 (没有父类
Class clazz = Person.class;
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f : declaredFields){
    System.out.println(f);
    //private java.lang.String com.atguigu.java1.Person.name
    //int com.atguigu.java1.Person.age
    //public int com.atguigu.java1.Person.id
}
  • 获取权限修饰符 getModifiers()
Class clazz = Person.class;
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f : declaredFields){
    int modifiers = f.getModifiers();
    System.out.print(Modifier.toString(modifiers));
}
  • 获取数据类型 getType()
Class type = f.getType();
  • 获取变量名 getName()
String fName = f.getName();
  • 获取方法 getMethods() 获取当前运行时类及其所有父类中声明为public权限的方法
Class clazz = Person.class;
Method[] methods = clazz.getMethods();
for (Method m : methods){
    System.out.println(m);
}

[这里出现了m, 用于后面的例子]

  • 获取方法 getDeclaredMethods() 获取当前运行时类中声明的所有方法 (不包含父类
Method[] declaredMethods = clazz.getDeclaredMethods();
  • 获取方法声明的注解 getAnnotations()
Class clazz = Person.class;
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method m : declaredMethods){
	Annotation[] annos = m.getAnnotations();
	for (Annotation a : annos){
		System.out.println(a);
	}
}
  • 获取权限修饰符 getModifiers()
System.out.print(Modifier.toString(m.getModifiers()));
  • 获取返回值类型 getReturnType()
System.out.print(m.getReturnType().getName());
  • 获取方法名 getName()
System.out.print(m.getName());
  • 获取形参列表 getParameterTypes()
Class[] parameterTypes = m.getParameterTypes();
  • 获取异常 getExceptionTypes()
Class[] exceptionTypes = m.getExceptionTypes();
  • 获取构造器 getDeclaredConstructors()getConstructors()
Class clazz = Person.class;
//所有构造器
Constructor[] constructors = clazz.getDeclaredConstructors();
for (Constructor c : constructors){
    System.out.println(c);
}
System.out.println();
//自己的构造器
Constructor[] constructors1 = clazz.getDeclaredConstructors();
for (Constructor c : constructors1){
    System.out.println(c);
}
  • 获取运行时的父类 getSuperclass()
Class superclass = clazz.getSuperclass();
  • 获取带泛型的父类 getActualTypeArguments()
Class clazz = Person.class;
Type superclass = clazz.getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType) superclass;
//获取泛型的类型
Type[] actualTypeArguments = paramType.getActualTypeArguments();
//方式一
System.out.println(actualTypeArguments[0].getTypeName());//java.lang.String
//方式二
System.out.println(((Class) actualTypeArguments[0]).getName());//java.lang.String
  • 获取运行时自己类接口 getInterfaces()
Class clazz = Person.class;
Class[] interfaces = clazz.getInterfaces();
for (Class c : interfaces){
    System.out.println(c);
}
  • 获取运行时父类接口 getSuperclass().getInterfaces()
Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
for (Class c : interfaces1){
    System.out.println(c);
}
  • 获取运行时类所在的包 getPackage()
Class clazz = Person.class;
Package aPackage = clazz.getPackage();
System.out.println(aPackage);
  • 获取类的注解 getAnnotations()
Annotation[] annotations = clazz.getAnnotations();
属性结构(public)getFields()方法名getName()
属性结构(自己)getDeclaredFields()形参列表getParameterTypes()
权限修饰符getModifiers()异常getExceptionTypes()
数据类型getType()注解getAnnotations()
变量名getName()构造器(public)getConstructors()
方法(public)getMethods()构造器(自己)getDeclaredConstructors()
方法(自己)getDeclaredMethods()父类getSuperclass()
方法声明的注解m.getAnnotations()带泛型的父类getActualTypeArguments()
权限修饰符getModifiers()接口getInterfaces()
返回值类型getReturnType()getPackage()

调用

  • 属性
@Test
public void testField() throws Exception {
    Class clazz = Person.class;
    //创建运行时类的对象
    Person p = (Person) clazz.newInstance();
    //获取指定的属性,要求运行时类中属性声明为public,通常不采用此方法
    Field id = clazz.getField("id");
    //设置当前属性的值
    id.set(p, 1001);
    //获取当前属性的值
    int pId = (int)id.get(p);
    System.out.println(pId);//1001
}
@Test
public void testField1() throws Exception{
    Class clazz = Person.class;
    Person p = (Person) clazz.newInstance();
    Field name = clazz.getDeclaredField("name");
    name.setAccessible(true);
    name.set(p,"慢慢");
    System.out.println(name.get(p));//慢慢
}
  • 方法
@Test
public void test1() throws Exception{
    Class clazz = Person.class;
    //创建运行时类的对象
    Person p = (Person) clazz.newInstance();
    //参数1:方法名  参数2:形参列表
    Method show = clazz.getDeclaredMethod("show", String.class);
    //私有开锁
    show.setAccessible(true);
    //参数1:方法的调用者  参数2:实参
    show.invoke(p,"CHN");//我的国籍是CHN
    //返回值
    Object returnValue = show.invoke(p,"CHN");
    System.out.println(returnValue);//CHN

    //如何调用静态方法
    Method showDesc = clazz.getDeclaredMethod("showDesc");
    showDesc.setAccessible(true);
    Object returnVal = showDesc.invoke(Person.class);
    System.out.println(returnVal);//null
}
  • 构造器
@Test
public void test2() throws Exception{
    Class clazz = Person.class;
    //private Person(String name)
    //1.获取构造器
    Constructor con = clazz.getDeclaredConstructor(String.class);
    //2.保证可访问
    con.setAccessible(true);
    //调用此构造器,创建运行时类的对象
    Object tom = con.newInstance("Tom");
    System.out.println(tom);//Person{name='Tom', age=0, id=0}
}

私有的要用.setAccessible(true)

Java反射主要是通过 `java.lang.Class` 类来实现的,该类提供了一系列方法来获取类的信息、创建对象、调用方法等操作。下面是一些常用的反射方法: 1. 获取Class对象 获取Class对象的方法有三种: - 调用对象的 `getClass()` 方法 - 使用 `.class` 语法 - 使用 `Class.forName()` 方法 例如,下面的代码可以获取 `String` 类的 `Class` 对象: ```java String str = "Hello, World!"; Class<?> clazz1 = str.getClass(); Class<?> clazz2 = String.class; Class<?> clazz3 = Class.forName("java.lang.String"); ``` 2. 创建对象 通过反射机制可以动态地创建对象,使用以下代码: ```java // 获取Class对象 Class<?> clazz = Class.forName("com.example.MyClass"); // 创建对象 Object obj = clazz.newInstance(); ``` 其中,`clazz.newInstance()` 方法会调用类的无参构造方法来创建对象。 3. 调用方法 通过反射机制可以动态地调用对象的方法,使用以下代码: ```java // 获取Class对象 Class<?> clazz = Class.forName("com.example.MyClass"); // 创建对象 Object obj = clazz.newInstance(); // 获取方法 Method method = clazz.getDeclaredMethod("myMethod", String.class); // 调用方法 method.invoke(obj, "Hello, World!"); ``` 其中,`getDeclaredMethod()` 方法可以获取指定名称和参数类型的方法,`invoke()` 方法可以调用方法并传入参数。 以上是Java反射的基本用法,通过反射可以实现很多动态化的操作,但需要注意反射会对性能产生一定的影响,需要谨慎使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值