Java反射与类加载

目录

一.类加载的过程

加载

链接

初始化

二.什么时候会发生类的初始化

1.类的主动引用(一定会发生类的初始化)

2.类的被动引用(不会发生类的初始化)

三.类加载器的作用

四.双亲委派机制

1.什么是双亲委派机制

2.双亲委派的作用

五.什么是反射

1.反射的优缺点

2.反射机制的应用场景有哪些

3.获取Class实例的方式

六.获取类的信息

一.获取类的属性

二.获取类的方法 

三.获取类的构造器

七.通过反射创建类的对象实例

1.通过无参构造创建对象

2.通过有参构造创建对象

3.通过反射调用对象的方法

4.通过反射调用类的属性

八.自定义注解与反射实现功能


一.类加载的过程

加载

  • 把类加载到内存中,并产生一个与之对应的class对象

链接

  •  给static资源分配空间,会在方法区进行分配

初始化

  •  执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。
  • 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
  • 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。

二.什么时候会发生类的初始化

1.类的主动引用(一定会发生类的初始化)

  • 当虚拟机启动,先初始化main方法所在的类
  • new一个类的对象
  • 调用类的静态成员(除了final常量)和静态方法
  • 使用java.lang.reflect包的方法对类进行反射调用
  • 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类

2.类的被动引用(不会发生类的初始化)

  • 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化
  • 通过数组定义类引用,不会触发此类的初始化
  • 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)

三.类加载器的作用

类加载器作用是用来把类(class)装载进内存的。JVM规范定义了如下类型的类的加载器。从下网上检查,从上往下加载。

  • 根类加载器:用C++编写的,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取。
  • 扩展类加载器:负责jre/liblext目录下的jar包或-Djava.ext.dirs指定目录下的jar包装入工作库
  • 系统类加载器:负责java-classpath或-Djava.class.path所指的目录下的类与jar包装入工作,是最常用的加载器
  • 自定义类加载器

四.双亲委派机制

1.什么是双亲委派机制

当一个Hello.class这样的文件要被加载时。不考虑我们自定义类加载器,首先会AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理也会先检查自己是否已经加载过,如果没有再往上。注意这个类似递归的过程,直到到达Bootstrap classLoader之前,都是在检查是否加载过,并不会选择自己去加载。直到BootstrapClassLoader,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。那么有人就有下面这种疑问了?

2.双亲委派的作用

防止我们的系统类被篡改,比如java.lang.String这个类,如果我们自己定义一个和这个同名的类,通过双亲委派机制会先检查我们的加载器是否已经加载过该类,若加载过则不会再加载我们自定义的这个同名类。因为它被根类加载器已经加载过了,所以其他类加载器并没有机会再去加载,从一定程度上防止了危险代码的植入。

五.什么是反射

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

  • 类加载后,类的整个结构都会被封装在class对象中
  • 一个类在内存中只有一个class对象

1.反射的优缺点

  • 优点: 运行期类型的判断,动态加载类,提高代码灵活度。
  • 缺点: 性能瓶颈:反射相当于一系列解释操作,相比于直接调用反射有比较大的性能消耗;内部暴露和安全隐患。

反射到底慢在哪里?

  • Class.forName()方法底层调用了native方法
  • newInstance()方法每执行一遍就要进行一次安全检查

2.反射机制的应用场景有哪些

反射是框架设计的灵魂。

在我们平时的项目开发过程中,基本上很少会直接使用到反射机制,但这不能说明反射机制没有用,实际上有很多设计、开发都与反射机制有关,例如模块化的开发,通过反射去调用对应的字节码;动态代理设计模式也采用了反射机制,还有我们日常使用的 Spring/Hibernate 等框架也大量使用到了反射机制。

举例:①我们在使用JDBC连接数据库时使用Class.forName()通过反射加载数据库的驱动程序;②Spring框架也用到很多反射机制,最经典的就是xml的配置模式。Spring 通过 XML 配置模式装载 Bean 的过程:1) 将程序内所有 XML 或 Properties 配置文件加载入内存中; 2)Java类里面解析xml或properties里面的内容,得到对应实体类的字节码字符串以及相关的属性信息; 3)使用反射机制,根据这个字符串获得某个类的Class实例; 4)动态配置实例的属性
 

3.获取Class实例的方式

1.若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高。

//方式一:通过 类.class
Class c3 = Student.class;
System.out.println(c3.hashCode());

2.已知某个类的实例,调用该实例的getClass()方法获取Class对象

//方式二:通过 类的对象.getClass()
Student stu = new Student();
Class c2 = stu.getClass();
System.out.println(c2.hashCode());

3.已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException

//方式三:通过 Class.forName("全类名")
Class c1 = Class.forName("com.ws.Student");
System.out.println(c1.getName());

4.基本类型的包装类都有一个TYPE属性

Class c4 = Integer.TYPE;
System.out.println(c4.getName());

5.通过子类class的getSuperclass()方法

//Student 继承 Person
Class s1 = Student.class;
Class p1 = s1.getSuperclass();

6.通过ClassLoader

//方式四 通过类加载器  

//Demo.class.getClassLoader是为了得到一个类加载器 Demo是当前的类

Class cc = Demo.class.getClassLoader.loadClass("com.ws.Student");

六.获取类的信息

*如果要修改私有属性信息那么我们需要放开权限 setAccessible(true)

一.获取类的属性

//getFields()获取自己和父类中public修饰的属性 private修饰的属性就找不到
Field[] fields = c1.getFields();
for (Field field : fields) {
    System.out.println(field);
}
//getDeclaredFields()获取自己的所有属性 包括private  但是无法获取父类的属性
System.out.println("----------");
fields = c1.getDeclaredFields();
for (Field field : fields) {
    System.out.println(field);
}

结果:

public java.lang.String com.ws.Student.name
----------
public java.lang.String com.ws.Student.name
private int com.ws.Student.no
private int com.ws.Student.age

二.获取类的方法 

        //获取类的方法
        //getMethods()获得该类和父类的所有public方法
        Method[] methods = c1.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("----------");
        //getDeclaredMethods()获得该类的所有方法
        methods = c1.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }

结果:

public java.lang.String com.ws.Student.name
private int com.ws.Student.no
private int com.ws.Student.age
public java.lang.String com.ws.Student.toString()
public void com.ws.Student.setAge(int)
public int com.ws.Student.getNo()
public void com.ws.Student.setNo(int)
public int com.ws.Student.getAge()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
----------
public java.lang.String com.ws.Student.toString()
public void com.ws.Student.setAge(int)
private void com.ws.Student.printHello()
public int com.ws.Student.getNo()
public void com.ws.Student.setNo(int)
public int com.ws.Student.getAge()

三.获取类的构造器

        //获取类的构造器
        Constructor[] constructors = c1.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }

七.通过反射创建类的对象实例

1.通过无参构造创建对象

        //通过反射创建类的对象
        Student student = (Student)c1.newInstance();
        System.out.println(student);

**本质是调用了类的无参构造,若该类没有无参构造就会报错

2.通过有参构造创建对象

        //反射通过有参构造创建对象
        Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        Student student1 = (Student)constructor.newInstance("ws", 1001, 21);
        System.out.println(student1);

3.通过反射调用对象的方法

        //通过反射创建类的对象
        Student student = (Student)c1.newInstance();
        System.out.println(student);
        //通过反射调用对象的普通方法
        Method setAge = c1.getDeclaredMethod("setAge", int.class);
        //invoke():激活该方法
        setAge.invoke(student,20);
        System.out.println(student);

4.通过反射调用类的属性

        //public属性 name
        Field name = c1.getDeclaredField("name");
        name.set(student,"ws");
        System.out.println(student);
        //private属性 no
        Field no = c1.getDeclaredField("no");
        //不能直接操作对象的私有属性
        //setAccessible(true)如果要修改私有属性信息那么我们需要放开权限
        no.setAccessible(true);
        no.set(student, 101);
        System.out.println(student);

八.自定义注解与反射实现功能

自定义注解和反射实现功能

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

w7486

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值