类的加载器和反射

类的加载

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。

 加载

就是指将class文件读入内存,并为之创建一个Class对象。

任何类被使用时系统都会建立一个Class对象

  连接

验证 是否有正确的内部结构,并和其他类协调一致

准备 负责为类的静态成员分配内存,并设置默认初始化值

解析 将类的二进制数据中的符号引用替换为直接引用

类的初始化时机

1. 创建类的实例

2. 类的静态变量,或者为静态变量赋值

3. 类的静态方法

4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象

5. 初始化某个类的子类

6. 直接使用java.exe命令来运行某个主类

类加载器:

负责将.class文件加载到内存中,并为之生成对应的Class对象。

类加载器的组成

1.  Bootstrap ClassLoader 根类加载器

也被称为引导类加载器,负责Java核心类的加载

比如System,String等。在JDK中JRE的lib目录下rt.jar文件中

2. Extension ClassLoader 扩展类加载器

负责JRE的扩展目录中jar包的加载。

在JDK中JRE的lib目录下ext目录

3.  System ClassLoader 系统类加载器

负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径

反射

Class类

获取Class对象的三种方式

    //1通过Object类中的getObject()方法
        Person p=new Person();
        Class c=p.getClass();
        System.out.println(c);
        //2通过类名获取,类名.class 获取到字节码文件对象
        Class c1=Person.class;
        System.out.println(c1);
        System.out.println(c==c1);//true
        System.out.println(c.equals(c1));//true
        //3通过Class类的静态方法forName(包名.类名)获取
        Class c2 = Class.forName("com.oracle.DEMO.Person");// 包名.类名
        System.out.println(c2);

Person类:

package com.oracle.DEMO;

public class Person {
    public String name;
    private int age;
    public Person(){
        System.out.println("公共构造方法");
    }
    public Person(String name,int age){
        this.name=name;
        this.age=age;
        System.out.println("公共有参构造");
    }
    private Person(int age,String name){
        this.name=name;
        this.age=age;
        System.out.println("私有有参构造");
    }
    public void eat(){
        System.out.println("公共方法");
    }
    public void sleep(String name){
        this.name=name;
        System.out.println(name+"公共有参方法");
    }
    private void playGames(){
        System.out.println("私有有参方法");
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }

}

通过反射获取构造方法并使用

  在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示。其中,构造方法使用类Constructor表示。可通过Class类中提供的方法获取构造方法:

返回一个构造方法

   public Constructor<T> getConstructor(Class<?>... parameterTypes) 获取public修饰, 指定参数类型所对应的构造方法

   public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取指定参数类型所对应的构造方法(包含私有的)

返回多个构造方法

    public Constructor<?>[] getConstructors() 获取所有的public 修饰的构造方法

    public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法(包含私有的)

获取构造方法:

//获取构造方法
    public static void method2() throws Exception{
        //获取字节码文件对象
        Class c = Class.forName("com.oracle.DEMO.Person");
        /*Constructor[] cons=c.getConstructors();
        //h=获取非私有构造方法数组
        for(Constructor con:cons){
            System.out.println(con);
        }*/
    /*    //获取空参构造
        Constructor con=c.getConstructor();
//        System.out.println(con);
        //使用空参构造创建对象
        Object obj = con.newInstance();
        Person p=(Person)obj;
        p.eat();
//        System.out.println(obj);
*/
        /*//获取有参构造并调用
        Constructor con1 = c.getConstructor(String.class,int.class );
        Object obj = con1.newInstance("张三",13);
        System.out.println(obj);*/
        //针对空参构造有一个快捷创建对象的方式
        //1Person类中必须有空参构造方法
        //2.空参构造必须是public修饰’
        Object obj = c.newInstance();
        System.out.println(obj);
    }

通过反射方式,获取构造方法(包括私有),创建对象

public static void method3() throws Exception{
        Class c = Class.forName("com.oracle.DEMO.Person");
        /*//获得所有构造方法,包括私有
        Constructor[] con = c.getDeclaredConstructors();
        for(Constructor co:con){
            System.out.println(co);
        }*/
        //获取私有构造方法并使用
        Constructor con = c.getDeclaredConstructor(int.class,String.class);
        con.setAccessible(true);//取消Java检查,通过反射获取私有构造---暴力反射,不推荐使用
        //破坏了程序的封装性和安全性,好比抢银行
        Object obj = con.newInstance(13,"王五");
        System.out.println(obj);

    }

通过反射,创建对象,获取指定的成员变量,进行赋值与获取值操作

返回一个成员变量

   public Field getField(String name) 获取指定的 public修饰的变量

    public Field getDeclaredField(String name) 获取指定的任意变量

返回多个成员变量

   public Field[] getFields() 获取所有public 修饰的变量

    public Field[] getDeclaredFields() 获取所有的 变量 (包含私有)

public static void method4()  throws Exception{
        Class c = Class.forName("com.oracle.DEMO.Person");
        Object obj = c.newInstance();
        //获取公共成员变量并使用
        /*Field field=c.getField("name");
        field.set(obj,"张三");*/
//        System.out.println(obj);
        //获取私有成员并使用
        Field f=c.getDeclaredField("age");
        f.setAccessible(true);
        f.set(obj, 18);
        Field f1=c.getDeclaredField("name");
        f1.setAccessible(true);
        f1.set(obj, "小王");
        System.out.println(f.get(obj));
        System.out.println(f1.get(obj));

    }

获取成员方法并使用:

    public static void method5()  throws Exception{
        //获取字节码文件对象
        Class c = Class.forName("com.oracle.DEMO.Person");
        Object obj = c.newInstance();
        //获取空参的成员方法 并运行
        Method method=c.getMethod("eat");
        method.invoke(obj);
        //获取有参成员方法并运行
        Method method1=c.getMethod("sleep",String.class);
        method1.invoke(obj,"张三");

    }

泛型擦除

  程序编译后产生的.class文件中是没有泛型约束的,这种现象我们称为泛型的擦除。

package com.oracle.DEMO;

import java.lang.reflect.Method;
import java.util.ArrayList;

public class demo02 {

    public static void main(String[] args)throws Exception {
        //泛型擦除,泛型不进class文件
        ArrayList<String> arr=new ArrayList<String>();
        arr.add("1");
        Class c = arr.getClass();
        Method method1=c.getMethod("add",Object.class);

        method1.invoke(arr, 1.5);
        System.out.println(arr);
    }

}

反射配置文件

测试类:

package com.oracle.DeGai;

import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.Properties;

public class Test {

    public static void main(String[] args) throws Exception {
//        new Person().eat();
        //反射配置文件实现
        //把我们要运行的类和方法,以键值对的形式写在文本中
        //运行哪个类的方法,只需要改配置文件即可
        //1准备配置文件,键值对
        //2.IO读取配置文件,Reader
        //3.将文件中的键值对保存在Properties集合中,键值对,就i是类和方法名
        //4.通过反射获取指定类的class文件对象
        //5.通过class文件对象获取指定方法
        //6.运行方法
        FileReader fr=new FileReader("src/config.properties");
        Properties pro=new Properties();
        pro.load(fr);
        String className=pro.getProperty("className");
        String methodName=pro.getProperty("methodName");
        //获取字节码文件对象
        Class c = Class.forName(className);
        Object obj = c.newInstance();
        Method method=c.getMethod(methodName);
        method.invoke(obj);
    }

}

配置文件内容例子:

className=com.oracle.DeGai.Student
methodName=study

原文地址:https://www.cnblogs.com/lzw123-/p/9561531.html

时间: 2024-11-05 14:42:10

类的加载器和反射的相关文章

java 27 - 1 反射之 类的加载器

说到反射,首先说类的加载器. 类的加载: 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化. 加载: 就是指将class文件读入内存,并为之创建一个Class对象. 任何类被使用时系统都会建立一个Class对象. 连接: 验证 是否有正确的内部结构,并和其他类协调一致 准备 负责为类的静态成员分配内存,并设置默认初始化值 解析 将类的二进制数据中的符号引用替换为直接引用 初始化: 就是我们以前讲过的初始化步骤 类初始化时机: 创建类的实

Day18 (一)类的加载器

一个运行时的Java虚拟机(JVM)负责运行一个Java程序. 当启动一个Java程序时,一个虚拟机实例诞生:当程序关闭退出,这个虚拟机实例也就随之消亡. 如果在同一台计算机上同时运行多个Java程序,将得到多个Java虚拟机实例,每个Java程序都运行于它自己的Java虚拟机实例中. 在如下几种情况下,Java虚拟机将结束生命周期: 1.执行了System.exit()方法 2.程序正常执行结束 3.程序在执行过程中遇到了异常或错误而异常终止 4.由于操作系统出现错误而导致Java虚拟机进程终

【深入理解Java虚拟机 】类的加载器

1. 类加载器的分类 JVM 自带的类加载器 根类加载器( BootStrap ) 拓展类加载器 ( Extension ) 系统 (应用) 加载器 ( System / AppClassLoader) 开发者自己创建的类加载器 java.long.ClassLoader 的子类 public abstract class ClassLoader { // 抽象类,不能实例化吗,需要继承并重写其方法 } 2. 加载时机 类加载器不是在 "首次主动" 使用的时候采取尝试加载一般情况下回提

类的加载机制和反射——一、类的加载、连接和初始化

类的加载.连接和初始化 1.JVM和类 (1)当调用java命令运行某个Java程序时,该命令会启动一个Java虚拟机进程,不管该Java程序有多么复杂,该程序启动了多少线程,他们都处于该Java虚拟机进程里. (2)同一个JVM的所有线程.所有变量都处于同一个进程里,他们都使用该JVM进程的内存区. 下面的ATast1和ATest2的输出结果分别是7和6,因为这两个不是位于同一个JVM中的. public class A { //定义该类的类变量 public static int a = 6

类的加载器 ClassLoader

先说明类的加载过程: 当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化: 而关于ClassLoader: 类加载器是用来把类(class)装载进内存的.JVM 规范定义了两种类型的类加载器:启动类加载器(bootstrap)和用户自定义加载器(user-defined class loader). JVM在运行时会产生3个类加载器组成的初始化加载器层次结构 ,如下图所示: 举例如下: public class TestClassLoader { pu

类自动加载器

1 /** 2 * Created by [中弘集团] qq 812035863 . 3 * User: Taoist 4 * Date: 2015/7/9 5 * Time: 20:57 6 * description: 自动类加载 7 * 入口文件首先载入加载器 8 * 9 * 加载文件要放在类库内才有作用 10 * 11 * 如果 root 是根目录 12 * 13 * /root/library 是库目录 14 * 15 * autoload文件放在library 下面 16 * 17

java 自定义类的加载器

首先介绍自定义类的应用场景: (1)加密:Java代码可以轻易的被反编译,如果你需要把自己的代码进行加密以防止反编译,可以先将编译后的代码用某种加密算法加密,类加密后就不能再用Java的ClassLoader去加载类了,这时就需要自定义ClassLoader在加载类的时候先解密类,然后再加载. (2)从非标准的来源加载代码:如果你的字节码是放在数据库.甚至是在云端,就可以自定义类加载器,从指定的来源加载类. (3)以上两种情况在实际中的综合运用:比如你的应用需要通过网络来传输 Java 类的字节

类的加载机制和反射——五、使用反射生成JDK动态代理

使用反射生成JDK动态代理 1.使用Proxy和InvocationHandler创建动态代理 (1)Proxy提供了用于创建动态代理类和动态代理对象的静态方法,他也是所有动态代理类的父类. (2)如果在程序中为一个或多个接口动态的生成实现类,就可以使用Proxy来创建动态代理类,如果需要为一个或多个接口动态的创建实例,也可以使用Proxy来创建动态代理实例. (3)Proxy提供了两个方法来创建动态代理类和动态实例: 1)static Class<?> getProxyClass(Class

java 反射,类的加载过程以及Classloader类加载器

首先自定义一个类Person package reflection; public class Person { private String name; public int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int a