类加载、Java反射

类加载

当程序主动使用某个类时,如果该类还未被加载到内存中,则JVM会通过加载、连接、初始化3个步骤来对该类进行初始化。如果没有意外,JVM将会连续完成3个步骤,所以有时也把这个3个步骤统称为类加载或类初始化。
在这里插入图片描述

一、类加载过程

1.加载

加载指的是将类的class文件读入到内存,并为之创建一个java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象。类的加载由类加载器完成,类加载器通常由JVM提供,这些类加载器也是前面所有程序运行的基础,JVM提供的这些类加载器通常被称为系统类加载器。除此之外,开发者可以通过继承ClassLoader基类来创建自己的类加载器。 通过使用不同的类加载器,可以从不同来源加载类的二进制数据,通常有如下几种来源。

  1. 从本地文件系统加载class文件。
  2. 从JAR包加载class文件,这种方式也是很常见的,JDBC编程时用到的数据库驱动类就放在JAR文件中,JVM可以从JAR文件中直接加载该class文件。
  3. 通过网络加载class文件。
  4. 把一个Java源文件动态编译,并执行加载。

类加载器通常无须等到“首次使用”该类时才加载该类,Java虚拟机规范允许系统预先加载某些类。

2.链接

当类被加载之后,系统为之生成一个对应的Class对象,接着将会进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中。类连接又可分为如下3个阶段。
1)验证:验证阶段用于检验被加载的类是否有正确的内部结构,并和其他类协调一致。Java是相对C++语言是安全的语言,例如它有C++不具有的数组越界的检查。这本身就是对自身安全的一种保护。验证阶段是Java非常重要的一个阶段,它会直接的保证应用是否会被恶意入侵的一道重要的防线,越是严谨的验证机制越安全。验证的目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身安全。其主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证。
四种验证做进一步说明:

  • 文件格式验证:主要验证字节流是否符合Class文件格式规范,并且能被当前的虚拟机加载处理。例如:主,次版本号是否在当前虚拟机处理的范围之内。常量池中是否有不被支持的常量类型。指向常量的中的索引值是否存在不存在的常量或不符合类型的常量。

  • 元数据验证:对字节码描述的信息进行语义的分析,分析是否符合java的语言语法的规范。

  • 字节码验证:最重要的验证环节,分析数据流和控制,确定语义是合法的,符合逻辑的。主要的针对元数据验证后对方法体的验证。保证类方法在运行时不会有危害出现。

  • 符号引用验证:主要是针对符号引用转换为直接引用的时候,是会延伸到第三解析阶段,主要去确定访问类型等涉及到引用的情况,主要是要保证引用一定会被访问到,不会出现类等无法访问的问题。

2)准备:为类变量(即static修饰的字段变量)分配内存并且设置该类变量的初始值。如static int i=5;这里只将 i 赋值为0,而在 初始化 时才会赋值为5。

注意:
1.这里不包含用final修饰的static,因为final在编译的时候就会分配了。
2.这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到Java堆中。

3)解析:将类的二进制数据中的符号引用替换成直接引用。

符号引用:符号引用是以一组符号来描述所引用的目标,符号可以是任何的字面形式的字面量,只要不会出现冲突能够定位到就行。布局和内存无关。
直接引用:是指向目标的指针,偏移量或者能够直接定位的句柄。该引用是和内存中的布局有关的,并且一定加载进来的。

3.初始化

类加载最后阶段,若该类具有超类,则对其进行初始化,执行静态初始化器和静态初始化成员变量(如前面只初始化了默认值的static变量将会在这个阶段赋值,成员变量也将被初始化)。

二、类加载的时机

  • 创建类的实例,也就是new一个对象
  • 访问某个类或接口的静态变量,或者对该静态变量赋值
  • 调用类的静态方法
  • 反射(Class.forName(“com.lyj.load”))
  • 初始化一个类的子类(会首先初始化子类的父类)
  • JVM启动时标明的启动类,即文件名和类名相同的那个类

特别的:
对于一个final类型的静态变量,如果该变量的值在编译时就可以确定下来,那么这个变量相当于“宏变量”。Java编译器会在编译时直接把这个变量出现的地方替换成它的值,因此即使程序使用该静态变量,也不会导致该类的初始化。
反之,如果final类型的静态Field的值不能在编译时确定下来,则必须等到运行时才可以确定该变量的值,如果通过该类来访问它的静态变量,则会导致该类被初始化。

三、三种类加载器

JVM预定义有三种类加载器,当一个 JVM启动的时候,Java开始使用如下三种类加载器。

  1. 启动(Bootstrap)类加载器
    启动类加载器主要加载的是JVM自身需要的类,这个类加载使用C++语言实现的,是虚拟机自身的一部分,它负责将 <JAVA_HOME>/lib路径下的核心类库或-Xbootclasspath参数指定的路径下的jar包加载到内存中,注意必由于虚拟机是按照文件名识别加载jar包的,如rt.jar,如果文件名不被虚拟机识别,即使把jar包丢到lib目录下也是没有作用的(出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类)。
  2. 扩展(Extension)类加载器
    扩展类加载器是指Sun公司实现的sun.misc.Launcher$ExtClassLoader类,由Java语言实现的,是Launcher的静态内部类,它负责加载<JAVA_HOME>/lib/ext目录下或者由系统变量-Djava.ext.dir指定位路径中的类库,开发者可以直接使用标准扩展类加载器。
  3. 系统(System)类加载器
    也称应用程序加载器。是指 Sun公司实现的sun.misc.Launcher$AppClassLoader。它负责加载系统类路径java -classpath或-D java.class.path 指定路径下的类库,也就是我们经常用到的classpath路径,开发者可以直接使用系统类加载器,一般情况下该类加载是程序中默认的类加载器,通过ClassLoader#getSystemClassLoader()方法可以获取到该类加载器。

在Java的日常应用程序开发中,类的加载几乎是由上述3种类加载器相互配合执行的,在必要时,我们还可以自定义类加载器,需要注意的是,Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存生成class对象,而且加载某个类的class文件时,Java虚拟机采用的类加载机制是双亲委派模式。

四、类加载机制

JVM的类加载机制主要有如下3种。

  • 双亲委派:所谓的双亲委派,则是先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父加载器,依次递归,如果父加载器可以完成类加载任务,就成功返回;只有父加载器无法完成此加载任务时,才自己去加载。
  • 全盘负责:所谓全盘负责,就是当一个类加载器负责加载某个Class时,该Class所依赖和引用其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入。
  • 缓存机制:缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区中搜寻该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓冲区中。这就是为很么修改了Class后,必须重新启动JVM,程序所做的修改才会生效的原因。

这里详细介绍一下双亲委派机制:

双亲委派机制的工作原理是:
如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载。

双亲委派机制的优势:
采用双亲委派模式的是好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。
其次是考虑到安全因素,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改。

五、class文件的显式加载与隐式加载

所谓class文件的显式加载与隐式加载的方式是指JVM加载class文件到内存的方式。
显式加载指的是在代码中通过调用ClassLoader加载class对象,如直接使用Class.forName(name)或this.getClass().getClassLoader().loadClass()加载class对象。
隐式加载则是不直接在代码中调用ClassLoader的方法加载class对象,而是通过虚拟机自动加载到内存中,如在加载某个类的class文件时,该类的class文件中引用了另外一个类的对象,此时额外引用的类将通过JVM自动加载到内存中。在日常开发以上两种方式一般会混合使用。

Java反射

前言:

反射的提出:
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。其中LEAD/LEAD++ 、OpenC++ 、MetaXa和OpenJava等就是基于反射机制的语言。最近,反射机制也被应用到了视窗系统、操作系统和文件系统中。

为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念:
静态编译:在编译时确定类型,绑定对象,即通过。
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。
一句话,反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中它的灵活性就表现的十分明显。
比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如 这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,
而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功 能。
它的缺点是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它 满足我们的要求。这类操作总是慢于只直接执行相同的操作。

一、Java反射的定义

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

简言之:反射就是把Java类中的各种成分映射成一个个的Java对象

二、Class类的API详解

Class类的API
在这里插入图片描述
Class 的实例表示正在运行的 Java 应用程序中的类和接口。JVM中的每一个类、每一个基本数据类型都有对应的Class对象。
Class 没有公共构造方法。Class 对象是在加载类时由JVM以及通过调用类加载器中的defineClass 方法自动构造的。

1.获取Class对象的四种方式
  1. 每个类、每种数据类型都有一个class属性,可以直接通过类名,数据类型名直接访问。例如:User.class int.class 等等。
  2. 通过对象实例的getClass()方法获取该创建对象的类的class对象。(Object类有getClass()方法,而所有的类都继承Object)
  3. Class的静态方法forName(String className)方法。[常用]
  4. 利用类加载器
User
package com.bluemsun;

public class User {
    private String name;
    private String password;
    private int age;
    public int userNum;
    protected String userName;

    public static void main(String[] args) {
        System.out.println("User的main方法执行.....");
    }

    protected String test1() {
        return "无参protected方法";
    }

    private int test2(int a) {
        System.out.println("有参private方法");
        return a;
    }

    void test3() {
        System.out.println("无参默认权限方法");
    }

    public int getUserNum() {
        return userNum;
    }

    public void setUserNum(int userNum) {
        this.userNum = userNum;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public User(String name, String password, int age) {
        this.name = name;
        this.password = password;
        this.age = age;
    }

    public User() {}

    public User(int age) {
        this.age = age;
    }

    protected User(String name) {
        this.name = name;
    }

    private User(String name,int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", password='" + password + '\'' +
                ", age=" + age +
                ", userNum=" + userNum +
                ", userName='" + userName + '\'' +
                '}';
    }
}

Demo1
package com.bluemsun;

public class Demo1 {
    public static void main(String[] args) {
        try {
            User user = new User();

            /**第一种获取方法
             * 每个类、每种数据类型都有一个class属性,可以直接通过类名,数据类型名直接访问。
             */
            Class<?> class1 = User.class;
            Class<?> classInt = int.class;
            System.out.println("--------第一种方式---------");
            System.out.println(class1);
            System.out.println(classInt);

            /**第二种获取方法
             * 通过对象实例的 getClass() 方法获取该创建对象的类的 class 对象。
             * (因为 Object 类有 getClass()方法,而所有的类都继承 Object)。
             */
            //这个意义不大,因为我们已经可以知道它是User类了
            Class<?> class2 = user.getClass();
            //而当我们接收到的是一个Object对象时,可以通过getClass()方法知道对象类型。这样使用比较多
            Object object = new User();
            Class<?> classObject = object.getClass();
            System.out.println("--------第二种方式---------");
            System.out.println(class2);
            System.out.println(classObject);

            /**第三种获取方式
             *  Class的静态方法 forName(String className) 方法。
             *  一般框架开发中这种用的比较多,因为配置文件中一般配的都是全类名,通过这种方式可以得到Class实例。
             */
            Class<?> class3 = Class.forName("com.bluemsun.User");//如果有包名一定要写全
            System.out.println("--------第三种方式---------");
            System.out.println(class3);

            /**第四种获取方式
             *  利用类的加载器
             */
            System.out.println("---------第四种方式----------");
            ClassLoader classLoader = Demo1.class.getClassLoader();
            Class class4 = classLoader.loadClass("com.bluemsun.User");
            System.out.println(class4);

            System.out.println("----------判断class1、class2、class3,class4是否相同------------");
            System.out.println(class1 == class2);
            System.out.println(class1 == class3);
            System.out.println(class1 == class4);

            System.out.println("--------获取当前类型的父类的类型-------");
            Class<?> classUserSuper = class1.getSuperclass();
            Class<?> classPeopleSuper = Class.forName("com.bluemsun.People").getSuperclass();
            System.out.println(classUserSuper);
            System.out.println(classPeopleSuper);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}



//控制台输出:

无参构造方法
--------第一种方式---------
class com.bluemsun.User
int
无参构造方法
--------第二种方式---------
class com.bluemsun.User
class com.bluemsun.User
--------第三种方式---------
class com.bluemsun.User
---------第四种方式----------
class com.bluemsun.User
----------判断class1、class2、class3,class4是否相同------------
true
true
true
--------获取当前类型的父类的类型-------
class com.bluemsun.People
class java.lang.Object

注意:
1、在运行期,一个类只产生唯一的一个Class对象。
2、通常使用第三种方法获取Class对象

2. 构造方法的获取与使用
  1. 获取批量的构造方法:
    public Constructor[] getConstructors()获取所有"公有的"构造方法。
    public Constructor[] getDeclaredConstructors()获取所有的构造方法(包括私有、受保护、默认、公有)。
  2. 获取单个的构造方法:
    public Constructor getConstructor(Class... parameterTypes)获取单个的"公有的"构造方法。
    public Constructor getDeclaredConstructor(Class... parameterTypes)获取"某个构造方法"可以是私有的,或受保护、默认、公有。
  3. 调用构造方法创建对象:
    Constructor对象的newInstance(Object... initargs)方法。
    注意setAccessible是启用和禁用访问安全检查的方法。由于JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查,破坏封装,可以达到提升反射速度的目的.
Demo2:
package com.bluemsun;

import java.lang.reflect.Constructor;

public class Demo2 {
    public static void main(String[] args) {
        try {
            Class classUser = Class.forName("com.bluemsun.User");

            System.out.println("---------获取所有公有构造方法----------");
            Constructor[] constructors1 = classUser.getConstructors();
            for (Constructor c : constructors1) {
                System.out.println(c);
            }

            System.out.println("----------获取所有构造方法-----------");
            Constructor[] constructors2 = classUser.getDeclaredConstructors();
            for (Constructor c : constructors2) {
                System.out.println(c);
            }

            System.out.println("---------------获取公有无参的构造方法-----------");
            Constructor constructor3 = classUser.getConstructor(null);
            System.out.println(constructor3);


            System.out.println("-------调用公有无参构造方法创建对象--------");
            Object object1 = constructor3.newInstance();
            System.out.println(object1);

            System.out.println("----------暴力访问私有构造方法--------");
            Class classUser1 = Class.forName("com.bluemsun.User1");
            Constructor constructor5 = classUser1.getDeclaredConstructor(String.class,int.class);
            constructor5.setAccessible(true);
            Object object2 = constructor5.newInstance("Jerry",19);
            System.out.println(object2);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


//控制台输出
---------获取所有公有构造方法----------
public com.bluemsun.User()
public com.bluemsun.User(java.lang.String,java.lang.String,int)
public com.bluemsun.User(int)
----------获取所有构造方法-----------
protected com.bluemsun.User(java.lang.String)
public com.bluemsun.User()
public com.bluemsun.User(java.lang.String,java.lang.String,int)
private com.bluemsun.User(java.lang.String,int)
public com.bluemsun.User(int)
---------------获取公有无参的构造方法-----------
public com.bluemsun.User()
-------调用公有无参构造方法创建对象--------
无参构造方法
User{name='null', password='null', age=0, userNum=0, userName='null'}
----------暴力访问私有构造方法--------
User1{name='Jerry', age=19}

User1:
package com.bluemsun;

public class User1 {
    String name;
    int age;

    //私有构造方法
    private User1(String name,int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User1{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

3.字段的访问与设置
  1. 批量获取
    Field[] getFields()获取所有的"公有字段" 。
    Field[] getDeclaredFields()获取所有字段,包括:私有、受保护、默认、公有。
  2. 获取单个
    public Field getField(String fieldName)获取某个"公有的"字段。
    public Field getDeclaredField(String fieldName)获取某个字段(可以是私有的)。
  3. 设置某个对象的变量值
    Field对象的public void set(Object obj,Object value)方法,object为字段对象,value为字段值
Demo3:
package com.bluemsun;

import java.lang.reflect.Field;

public class Demo3 {
    public static void main(String[] args) {
        try {
            Class classUser = Class.forName("com.bluemsun.User");

            System.out.println("-----获取所有公有属性---------");
            Field[] fields = classUser.getFields();
            for (Field f : fields) {
                System.out.println(f);
            }

            System.out.println("---------获取所有属性-------");
            Field[] fields1 = classUser.getDeclaredFields();
            for (Field f : fields1) {
                System.out.println(f);
            }

            System.out.println("-------获取公有单个字段------");
            Field field = classUser.getField("userNum");
            System.out.println(field);

            System.out.println("--------获取单个字段-------");
            Field field1 = classUser.getDeclaredField("name");
            System.out.println(field1);

            System.out.println("---------设置对象公有权限字段的值--------");
            Object object1 = Class.forName("com.bluemsun.User").getConstructor().newInstance();
            Field fieldObject1UserNum = classUser.getField("userNum");
            fieldObject1UserNum.set(object1,1);
            System.out.println(object1);

            System.out.println("----------设置对象的字段值------------");
            Object object = Class.forName("com.bluemsun.User").getConstructor().newInstance();
            Field fieldObjectName = classUser.getDeclaredField("name");
            fieldObjectName.setAccessible(true);
            fieldObjectName.set(object,"Tom");
            System.out.println(object);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

//输出
-----获取所有公有属性---------
public int com.bluemsun.User.userNum
---------获取所有属性-------
private java.lang.String com.bluemsun.User.name
private java.lang.String com.bluemsun.User.password
private int com.bluemsun.User.age
public int com.bluemsun.User.userNum
protected java.lang.String com.bluemsun.User.userName
-------获取公有单个字段------
public int com.bluemsun.User.userNum
--------获取单个字段-------
private java.lang.String com.bluemsun.User.name
---------设置对象公有权限字段的值--------
无参构造方法
User{name='null', password='null', age=0, userNum=1, userName='null'}
----------设置对象的字段值------------
无参构造方法
User{name='Tom', password='null', age=0, userNum=0, userName='null'}

4.成员方法的获取与调用
  1. 批量获取:
    public Method[] getMethods()获取所有"公有方法"。包含了父类的方法也包含Object类)。
    public Method[] getDeclaredMethods()获取所有的成员方法,包括私有的(不包括继承的)
  2. 获取单个的:
    getMethod(String name, Class<?>... parameterTypes)获取"公有方法"。name : 方法名, Class<?>… parameterTypes : 形参的Class类型对象
    gettDeclaredMethod(String name, Class<?>... parameterTypes)获取方法。
  3. 调用方法:
    Method对象的public Object invoke(Object obj,Object... args)方法。obj : 要调用方法的对象,args:调用方式时所传递的实参。
Demo4:
package com.bluemsun;

import java.lang.reflect.Method;

public class Demo4 {
    public static void main(String[] args) {

        try {
            Class classUser = Class.forName("com.bluemsun.User");

            System.out.println("------获取所有公有的成员方法------");
            Method[] methods = classUser.getMethods();
            for (Method m : methods) {
                System.out.println(m);
            }

            System.out.println("-------获取所有的成员方法--------");
            Method[] methods1 = classUser.getDeclaredMethods();
            for (Method m : methods1) {
                System.out.println(m);
            }

            System.out.println("-------获取单个公有的成员方法---------");
            Method method = classUser.getMethod("setUserNum",int.class);
            System.out.println(method);

            System.out.println("-------调用公有成员方法------");
            Object object = Class.forName("com.bluemsun.User").getConstructor().newInstance();
            method.invoke(object,1);
            System.out.println(object);

            System.out.println("-----调用成员方法------");
            Object object1 = Class.forName("com.bluemsun.User").getConstructor().newInstance();
            Method method1 = classUser.getDeclaredMethod("testProtected");
            Method method2 = classUser.getDeclaredMethod("testPrivate",int.class);
            Method methodMain = classUser.getMethod("main",String[].class);
            method1.setAccessible(true);
            method2.setAccessible(true);
            method1.invoke(object1);
            method2.invoke(object1,100);
            //static方法,所以可以直接传null;需要将String数组强转为3个对象
            methodMain.invoke(null,(Object) new String[] {"a","b","c"});
            System.out.println(object1);


            System.out.println("-----------调用父类的方法----------");
            Class classSuper = Class.forName("com.bluemsun.User").getSuperclass();
            Object obj = classUser.getConstructor().newInstance();
            Method method3 = classSuper.getMethod("test1");
            Method method4 = classSuper.getDeclaredMethod("test2");
            method4.setAccessible(true);
            method3.invoke(obj,null);
            method4.invoke(obj,null);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


//输出
------获取所有公有的成员方法------
public java.lang.String com.bluemsun.User.getName()
public static void com.bluemsun.User.main(java.lang.String[])
public void com.bluemsun.User.run()
public java.lang.String com.bluemsun.User.toString()
public void com.bluemsun.User.setName(java.lang.String)
public void com.bluemsun.User.setUserNum(int)
public java.lang.String com.bluemsun.User.getUserName()
public int com.bluemsun.User.getUserNum()
public java.lang.String com.bluemsun.User.eat()
public java.lang.String com.bluemsun.User.getPassword()
public int com.bluemsun.User.getAge()
public void com.bluemsun.User.setUserName(java.lang.String)
public void com.bluemsun.User.setAge(int)
public void com.bluemsun.User.setPassword(java.lang.String)
public java.lang.String com.bluemsun.People.getId()
public void com.bluemsun.People.test1()
public void com.bluemsun.People.setId(java.lang.String)
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() 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.bluemsun.User.getName()
public static void com.bluemsun.User.main(java.lang.String[])
public void com.bluemsun.User.run()
public java.lang.String com.bluemsun.User.toString()
public void com.bluemsun.User.setName(java.lang.String)
protected java.lang.String com.bluemsun.User.testProtected()
private int com.bluemsun.User.testPrivate(int)
public void com.bluemsun.User.setUserNum(int)
public java.lang.String com.bluemsun.User.getUserName()
public int com.bluemsun.User.getUserNum()
public java.lang.String com.bluemsun.User.eat()
void com.bluemsun.User.testDefault()
public java.lang.String com.bluemsun.User.getPassword()
public int com.bluemsun.User.getAge()
public void com.bluemsun.User.setUserName(java.lang.String)
public void com.bluemsun.User.setAge(int)
public void com.bluemsun.User.setPassword(java.lang.String)
-------获取单个公有的成员方法---------
public void com.bluemsun.User.setUserNum(int)
-------调用公有成员方法------
无参构造方法
User{name='null', password='null', age=0, userNum=1, userName='null'}
-----调用成员方法------
无参构造方法
无参protected方法
有参private方法
User的main方法执行.....
User{name='null', password='null', age=0, userNum=0, userName='null'}
-----------调用父类的方法----------
无参构造方法
Peoplepublic方法
Peopleprivate方法

5.获取对象的类型实现的接口、类加载器。Class类的具体实例
Demo5
package com.bluemsun;

public class Demo5 {
    public static void main(String[] args) {
        try {
            System.out.println("------获取该对象的类型所有实现的接口-------");
            Class<?>[] classUserInterfaces = Class.forName("com.bluemsun.User").getInterfaces();
            for (Class<?> c : classUserInterfaces) {
                System.out.println(c);
            }

            System.out.println("-------获取该对象的类型的类加载器-------");
            ClassLoader userLoader = Class.forName("com.bluemsun.User").getClassLoader();
            System.out.println(userLoader);

            /**
             *  Class类的具体的实例:
             *   (1)类:外部类,内部类
             *   (2)接口
             *   (3)注解
             *   (4)数组
             *   (5)基本数据类型
             *   (6)void
             */
            System.out.println("---------Class类的具体实例举例----------");
            int[] arr1 = new int[]{1,2,3,4};
            int[] arr2 = new int[]{5,6,7,8};
            Class class11 = User.class;
            Class class12 = Class.forName("com.bluemsun.Demo5$Test");
            Class class2 = Comparable.class;
            Class class3 = Override.class;
            Class class41 = arr1.getClass();
            Class class42 = arr2.getClass();
            Class class5 = int.class;
            Class class6 = void.class;
            System.out.println(class11+"\n"+class12+"\n"+class2+"\n"+class3+"\n"+class41+"\n"+class42+"\n"+class5+"\n"+class6);
            System.out.println("对于数组类型,同一个维度,同一个元素类型,得到的字节码就是同一个");
            System.out.println(class41 == class42);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public class Test {
        private int id;
        private String name;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "Test{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
}


//输出
------获取该对象的类型所有实现的接口-------
interface com.bluemsun.Eat
interface com.bluemsun.Run
-------获取该对象的类型的类加载器-------
jdk.internal.loader.ClassLoaders$AppClassLoader@66d3c617
---------Class类的具体实例举例----------
class com.bluemsun.User
class com.bluemsun.Demo5$Test
interface java.lang.Comparable
interface java.lang.Override
class [I
class [I
int
void
对于数组类型,同一个维度,同一个元素类型,得到的字节码就是同一个
true

三、反射的一些应用

1.用反射越过泛型检查

原理:泛型在编译期检查,编译过后泛型擦除(消失掉);而反射是在运行期。所以是可以通过反射越过泛型检查的。

Demo6
package com.bluemsun;

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

public class Demo6 {
    public static void main(String[] args) {
        try {
            ArrayList<Integer> arrayList = new ArrayList<>();
            arrayList.add(100);
            arrayList.add(200);
            Class arrClass = arrayList.getClass();
            Method m = arrClass.getMethod("add",Object.class);
            m.invoke(arrayList,"aaa");
            System.out.println(arrayList);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

//输出
[100, 200, aaa]

2.通过反射机制"修改"数组的大小
Demo7
package com.bluemsun;

import java.lang.reflect.Array;

public class Demo7 {

    public static void main(String[] args) {
        int[] arrayTest = new int[] {1,2,3,4,5};
        System.out.println("原来的长度:"+arrayTest.length);
        for (int a : arrayTest) {
            System.out.print(a);
            System.out.print(" ");
        }
        System.out.println();
        
        arrayTest = (int[])modifyLength(arrayTest,20);
        System.out.println("修改后的长度:"+arrayTest.length);
        for (int a : arrayTest) {
            System.out.print(a);
            System.out.print(" ");
        }
    }

    public static Object modifyLength(Object obj,int length) {
        try {
            Class arrComponentType = obj.getClass().getComponentType();
            Object newArr = Array.newInstance(arrComponentType,length);
            System.arraycopy(obj,0,newArr,0,Array.getLength(obj));
            return newArr;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

//输出
原来的长度:5
1 2 3 4 5 
修改后的长度:20
1 2 3 4 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 

3.通过反射运行配置文件内容

这种反射实现的主要功能是可配置和低耦合。只需要类名和方法名,而不需要一个类对象就可以执行一个方法。如果我们把全类名和方法名放在一个配置文件中,就可以根据调用配置文件来执行方法。例如我们·需要升级一个系统,底层的源码不需要改变,只需更新配置文件中的类名与方法名即可。

Demo8

package com.bluemsun;

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

public class Demo8 {
    public static void main(String[] args) {
        try {
            Class classTest = Class.forName(getValue("className"));
            Object object = classTest.getConstructor().newInstance();
            Method method = classTest.getMethod(getValue("methodName"));
            method.invoke(object);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static String getValue(String key) {
        Properties properties = new Properties();
        FileReader fileReader = null;
        try {
            fileReader = new FileReader("C:/Users/29688/IdeaProjects/FanSheDemos/src/test.properties");
            properties.load(fileReader);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                fileReader.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return properties.getProperty(key);
    }
}

//输出
无参构造方法
跑了1000m

思考:反射受否破坏了类的封装性?

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值