欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java基础之反射

程序员文章站 2022-11-21 09:52:30
java基础之反射 1. 类的加载、连接和初始化 1.1 类的加载 1.2 类的连接 1.3 类的初始化 1.4 类加载器 2. 反射 2.1 反射基本信息 2.1.1 Class对象 2.1.2 Java反射机制的类库支持 2.2 反射的基本实现 2.2.1 获取Class对象 2.2.2 获取构... ......

目录

java基础之反射

1. 类的加载、连接和初始化

1.1 类的加载

  当程序主动使用某个类时,如果该类还没有被加载到内存中,则系统会通过加载、连接、初始化这三个步骤对该类进行初始化。有时会把这一整个流程统称为类加载或类初始化。
  类加载指的是将类的class文件读入内存中,并为之创建一个 java.lang.class 对象,也就是说程序使用任何类的时候,都会为其创建一个class对象。

1.2 类的连接

  类被加载之后,系统会为之生成一个class对象,接着会进入连接阶段,连接阶段负责把类的二进制数据合并到jre中。类的连接又分为下面三个阶段:

  • 验证:确保被加载类的正确性
  • 准备:负责为类的静态成员分配内存,并设置默认初始化值
  • 解析:将类中的符号引用替换为直接引用

1.3 类的初始化

  在java中对类变量指定初始值得方法有两种:1. 声明类变量时指定初始值;2. 使用静态初始化块为类变量指定初始值。

  • 类加载的时机
  1. 创建类的实例的时候
  2. 访问类的静态变量的时候
  3. 调用类的静态方法的时候
  4. 使用反射方式来强制创建某个类或接口对应的java.lang.class对象
  5. 初始化某个类的子类的时候
  6. 直接使用java.exe命令来运行某个主类

1.4 类加载器

  类加载器负责将.class文件加载到内存中,并为之生成对应的class对象。类加载器负责加载所有的类,系统为所有加载到内存中的类生成一个java.lang.class 的实例。

类加载器的组成:

  • bootstrap classloader 根类加载器 : 也被称为引导类加载器,负责java核心类的加载,比如system类,在jdk中jre的lib目录下rt.jar文件中的类
  • extension classloader 扩展类加载器 : 负责jre的扩展目录中jar包的加载,在jdk中jre的lib目录下ext目录
  • system classloader 系统类加载器 : 负责在jvm启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径,主要是我们开发者自己写的类

2. 反射

  java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。
  反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等。

2.1 反射基本信息

  java程序中许多对象在运行时会出现两种类型:运行时类型编译时类型,例如person p = new student();这句代码中p在编译时类型为person,运行时类型为student。程序需要在运行时发现对象和类的真实信心。而通过使用反射程序就能判断出该对象和类属于哪些类。

2.1.1 class对象

  java文件被编译后,生成了.class文件,jvm此时就要去解读.class文件。当程序主动去使用某个类时,jvm会通过前面提到的三个步骤:加载、连接和初始化三个步骤对类进行初始化。被编译后的java文件.class也被jvm解析为一个对象,这个对象就是java.lang.class。这样当程序在运行时,每个java文件就最终变成了class类对象的一个实例。我们通过java的反射机制应用到这个实例,就可以去获得甚至去添加改变这个类的属性和动作,使得这个类成为一个动态的类。
  
  class类的概念尽管很抽象,但是无疑,它是反射机制的起源,是java语言中一个精巧美妙地设计。
  
  下面是翻译后的中文文档的描述:
  class类的实例表示正在运行的java应用程序的类和接口。枚举是一种类,注释(注解)是一种接口。每个数组属于被映射为class对象的一个类,所有具有相同元素类型和维数的数组都共享该class对象。基本的java类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 class 对象。class没有公用构造方法。class对象是在加载类时由jvm以及通过调用类加载器中的defineclass方法自动构造的。

2.1.2 java反射机制的类库支持

  在深入到反射机制之前,先探析一下反射机制的定义和应用。反射机制定义:java反射机制是在运行状态时,对于任意一个类,都能够直到这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。
  在java中,class类和java.lang.reflect类库一起构成了对java反射机制的支持。其中最常使用到的类是constructorfieldmethod,而这三个类都继承了一个接口java.lang.reflect.member

2.2 反射的基本实现

  实验类

//因篇幅原因,实验代码未展示set,get等等常用方法
public class cat {
    private string name = "";
    private string master = "";
    private int age = 0;

    public cat() {  }
    private cat(string name, string master) {   }
    public cat(string name, string master, int age) {   }   
    
    public void eat() {
        system.out.println("小鱼干真好吃~");
    }

    private void play() {
        system.out.println("快来陪我玩~");
    }

    @override
    public string tostring() {
        return "cat [name=" + name + ", master=" + master + ", age=" + age + "]";
    }
}

2.2.1 获取class对象

  在java中获取class对象有三种方法:

  • 通过类名获取:class c1 = student.class; 调用某各类的class属性来获取class对象。
  • 通过对象获取:class c2 = stu.getclass(); 通过getclass方法,该方法是object类下的一个方法。
  • 通过全类名获取:class c3 = class.forname("全限定类名"); 会有一个classnotfoundexception异常
public static void main(string[] args) throws exception {
    cat c = new cat();
    
    class cat1 = class.forname("algorithms.sort.cat");
    class cat2 = cat.class;
    class cat3 = c.getclass();

    system.out.println(cat1 == cat2);
    system.out.println(cat2 == cat3);
}
//输出结果
true
true

2.2.2 获取构造器并创建对象

  • getconstructors():返回了表示此类公共构造方法的constructor对象数组。
  • getdeclaredconstructors():这个方法返回constructor对象的所有构造方法。
      
      获取构造器
public static void main(string[] args) throws exception {
    class cat = cat.class;
    //获取所有公共构造方法
    constructor<?> cons[] = cat.getconstructors();
    for (constructor<?> con : cons) {
        system.out.println("getconstructors-----------" + con);
    }
    system.out.println("**************************************");
    //获取所有构造方法
    constructor<?> cons2[] = cat.getdeclaredconstructors();
    for (constructor<?> con2 : cons2) {
        system.out.println("getdeclaredconstructors---" + con2);
    }
}
//输出结果
getconstructors-----------public algorithms.sort.cat(java.lang.string,java.lang.string,int)
getconstructors-----------public algorithms.sort.cat()
**************************************
getdeclaredconstructors---public algorithms.sort.cat(java.lang.string,java.lang.string,int)
getdeclaredconstructors---private algorithms.sort.cat(java.lang.string,java.lang.string)
getdeclaredconstructors---public algorithms.sort.cat()

  
  创建对象

public static void main(string[] args) throws exception {
    class cat = cat.class;
    //使用公共构造器实例化对象
    constructor<?> cons1 = cat.getconstructor();
    //使用私有构造器实例化对象
    constructor<?> cons2 = cat.getdeclaredconstructor(string.class,string.class);
    cat cat1 = (cat)cons1.newinstance();
    //私有的构造方法反射后要打开权限才能进行相应操作
    cons2.setaccessible(true);
    cat cat2 = (cat)cons2.newinstance("tom","denny");
    system.out.println(cat1);
    system.out.println(cat2);
}
//输出结果
cat [name=, master=, age=0]
cat [name=tom, master=denny, age=0]

   在创建对象的过程中,值得注意的是如果反射的构造方法是私有的,那么要打开访问权限才能进行对象的实例化;也就是使用cons2.setaccessible(true);语句的原因。

2.2.3 获取成员变量和成员方法

  
  获取成员变量

public static void main(string[] args) throws exception {
    class cat = cat.class;
    //获取构造器
    constructor<?> cons = cat.getconstructor(string.class,string.class,int.class);
    //实例化对象
    cat cat1 = (cat)cons.newinstance("tom","denny",5);
    system.out.println(cat1);
    system.out.println("****************");
    
    field fields = cat.getdeclaredfield("name");
    //打开访问权限限制
    fields.setaccessible(true);
    fields.set(cat1, "jack");
    system.out.println(cat1);
}
//输出结果
cat [name=tom, master=denny, age=5]
****************
cat [name=jack, master=denny, age=5]

  获取成员方法

public static void main(string[] args) throws exception {
    class cat = cat.class;
    //获取构造器
    constructor<?> cons = cat.getconstructor(string.class, string.class, int.class);
    //实例化对象
    cat cat1 = (cat) cons.newinstance("tom", "denny", 5);
    system.out.println(cat1);
    system.out.println("****************");

    //获取私有和公共成员方法
    method method1 = cat.getdeclaredmethod("setname", string.class);
    method method2 = cat.getdeclaredmethod("eat");
    method1.setaccessible(true);
    method1.invoke(cat1, "petter");
    system.out.println(cat1);
    method2.invoke(cat1);
}
//输出结果
cat [name=tom, master=denny, age=5]
****************
cat [name=petter, master=denny, age=5]
小鱼干真好吃~

2.2.4 反射越过泛型检查

public static void main(string[] args) throws exception  {
    //泛型只在编译期进行检查,在运行期会被擦除
    arraylist<integer> list = new arraylist<>();
    list.add(111);
    list.add(222);
    //拿到字节码文件,字节码文件属于运行期
    class cla = class.forname("java.util.arraylist");
    method meth = cla.getmethod("add", object.class);
    meth.invoke(list, "abc");
    system.out.println(list);
}
//输出结果
[111, 222, abc]