JAVA反射知识点总结

1.反射的概念

1.1反射的出现背景

Java程序中,所有的对象都有两种类型:编译时类型和 运行时类型,而很多时候对象的编译时类型和运行时类型不一致。 Object obj= new String(“hello”);Object编译时类型 String运行时类型

例如:某些变量或形参的声明类型是Object类型,但是程序却需要调用该对象运行时类型的方法,该方法不是object中的方法,那么如何解决呢?

解决这个问题,有两种方案:

方案1:在编译和运行时都完全知道类型的具体信息,在这种情况下,我们可以直接先使用instanceof 运算符进行判断,再利用强制类型转换符将其转换成运行时类型的变量即可。

方案2:编译时根本无法预知该对象和类的真实信息,程序只能依靠 运行时信息来发现该对象和类的真实信息,这就必须使用反射。

希望动态的获取obj类的类型要

1.2反射概述

Reflection(反射)是被视为动态语言 的关键,反射机制允许程序在运行期间 借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

加载完类之后,在堆内存的方法区中就产生了一个class类型的对象(一个类只有一个class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。

1.3 Java反射机制研究及应用

Java反射机制是供的功能:

​ 在运行时判断任意一个对象所属的类

​ 在运行时构造任意一个类的对象

​ 在运行时判断任意一个类所具有的成员变量和方法

​ 在运行时获取泛型信息

​ 在运行时调用任意一个对象的成员变量和方法

​ 在运行时处理注解

​ 生成动态代理

1.4反射相关的主要API

java.lang.Class:代表一个类

java.lang.reflect.Method:代表类的方法

java.lang.reflect.Field:代表类的成员变量

java.lang.reflect.Constructor: 代表类的构造器

1.5反射的优缺点

优点:

​ 提高了Java程序的灵活性和扩展性,降低了耦合性,提高自适应能力

允许程序创建和控制任何类的对象,玩需提前 硬編码 目标类

缺点:

​ 反射的性能较低。

反射机制主要应用在对灵活性和扩展性要求很高的系统框架上

反射会模糊 程序内部逻辑,可读性较差。

1.6 class类的理解

针对于编写好的.java源文件进行编译(使用javac.exe),会生成一个或多个.class字节码文件。接着,我们使用java.exe命令对指定的.class文件进行解释运行。这个解释运行的过程中,我们需要将.class字节码文件加载(使用类的加载器)到内存中(存放在方法区)。加载到内存中的.class文件对应的结构即为Class的一个实例。

(通过.Class字节码文件创建实例)

比如:加载到内存中的Person类或String类或User类 ,都作为Class的一个一个的实例

Class clazz1 = Person.class;

Class clazz2 = String.class;

Class clazz3 = User. class;

运行时类在内存中会缓存,只能加载一次(地址永远都一样)

通过使用反射前后的例子的对比

1.面向对象中创建对象,调用指定结构(属性、方法)等功能,可以不使用反射,也可以使用反射。

区别是什么?

​ 不使用反射,需要考虑封装性。比如:出了Perosn类之后,就不能调用Person类中的私有的结构

使用反射,可以调用运行时类任意的构造器、属性、方法。包括私有的属性、方法、构造器。

2.以前创建对象并调用方法的方式,与现在通过反射创建对象并调用方法的方式对比有什么区别?哪种用的多?

​ 从程序可读性,开发者角度,开发中主要是完成业务代码,对于相关的对象方法调用都是确定的,所以使用非反射的方式极多。

​ 因为反射体现了动态性,(可以在运行时动态的获取对象所属的类,动态的调用相关的方法)所以在设计框架时,会大量的运用反射。

​ 框架 = 注解 +反射+ 设计模式

3.单例模式的饿汉式和懒汉式,私有化类的构造器!此时通过反射,可以创建单例模式中类的多个对象么

	public class SingletonEager {
// 私有静态成员变量,直接初始化
private static final SingletonEager instance = new SingletonEager();

// 私有构造方法,防止外部实例化
private SingletonEager() {
}

// 公共方法,提供全局访问点
public static SingletonEager getInstance() {
    return instance;
	}
}
public class SingletonLazy {
    // 私有静态成员变量,volatile 关键字确保多线程可见性
    private static volatile SingletonLazy instance;

    // 私有构造方法,防止外部实例化
    private SingletonLazy() {
    }

    // 公共方法,使用双重检查锁定(Double-Checked Locking)确保多线程安全
    public static SingletonLazy getInstance() {
        if (instance == null) {
            synchronized (SingletonLazy.class) {
                if (instance == null) {
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }
}

​ 是的!(其实也可以避免,看情况)

4.通过反射,可以调用类中私有的结构,是否与面向对象的封装性有冲突?是不是JAVA语言设计存在的BUG?

​ 不存在bug

​ 封装性:体现的是是否建议调用内部api的问题,比如private声明的结构,意味着不建议调用。

​ 反射:体现的是我们能否调用的问题。因为类的完整性都加载到内存中,所以我们就有能力调用。

​ 调用指定的属性(步骤)

步骤1.通过Class实例调用getDeclaredField(String fieldName),获取运行时类指定名的属性步骤2. setAccessible(true):确保此属性是可以访问的步骤

步骤3.通过Filed类的实例调用get(Object obj)(获取的操作)

​ 或 set(Object obj,Object value)(设置的操作)进行操作。

调用指定的方法

步骤1.通过CLass的实例调用getDecLaredMethod(String methodName,Class …args),获取指定的方法

步骤2. setAccessible(true):确保此方法是可访问的

步骤3.通过Method实例调用invoke(Object obj, Object…objs),即为对Method对应的方法的调用。

​ invoke()的返回值即Method对应的方法的返回值

​ 特别的:如果Method对应的方法的返回值类型为void,则invoke()返回值为null

3.3 调用指定的构造器(步骤)

步骤1.通过Class的实例调用getDeclaredConstructor(Class …args),获取指定参数类型的构造器

步骤2.setAcceSsible(true):确保此构造器是可以访问的

步骤3.通过Constructor实例调用newInstance(Object …objs〕,返回一个运行时类的实例。|

@Test
public void test4() throws ClassNotFoundException {
    //1.调用运行时类的静态属性
    Class<Person>pC1 = Person.class;

    //2.运行时类的对象的getClass();
    Person person = new Person();
    Class<? extends Person> pC2 = person.getClass();

    System.out.println(pC2.equals(pC1));//true

    //3.调用Class的静态方法forName(string className)
    // com.example.Person 全类名
    Class<?> pC3 = Class.forName("com.example.Person");

    //4.使用类的加载器方式
    //通过调用系统加载器的方式
    Class<?> pC4 = ClassLoader.getSystemClassLoader().loadClass(
            "com.example.Person");

}

你可能感兴趣的:(JAVA,java)