Java基础第20天 反射

类加载,反射

类加载器

类的加载

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

  • 加载

    就是指将 class 文件读入内存,并为之创建一个 Class 对象。 任何类被使用时系统都会建立一个 Class 对象

  • 连接

    验证 是否有正确的内部结构,并和其他类协调一致
    准备 负责为类的静态成员分配内存,并设置默认初始化值
    解析 将类的二进制数据中的符号引用替换为直接引用

  • 初始化

    初始化阶段,虚拟机负责对类的变量进行初始化
    声明类变量是指定初始值
    使用静态初始化块为类变量指定初始值

    使用:正常使用
    卸载:GC(垃圾回收机制)把无用对象从内存中卸载。

Class 类的初始化时机

Java 虚拟机只有在程序首次主动使用一个类或接口的时候才会初始化它。只有 6 种活动被看 作是程序对类和接口的主动使用:

  • 创建类的实例
  • 调用类的静态方法
  • 访问类或接口的静态变量,或者为静态变量赋值
  • 使用反射方式来强制创建某个类或接口对应的java.lang.Class 对象
  • 初始化某个类的子类
  • 直接使用 java.exe 命令来运行某个主类

类加载器

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

类加载器的组成
  • Bootstrap ClassLoader 根类加载器 也被称为引导类加载器,负责 Java 核心类的加载 比如 System、String 等。在 JDK 中 JRE 的 lib 目录下 rt.jar 文件中

  • Extension ClassLoader 扩展类加载器 负责 JRE 的扩展目录中 jar 包的加载。 在 JDK 中 JRE 的 lib 目录下 ext 目录

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

反射

JAVA 反射机制是在运行状态中:

  • 对于任意一个类,都能够知道这个类的所有属性和方法;
  • 对于任意一个对象,都能够调用它的任意一个方法和属性;

这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。

Class类

要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是 Class 类中的 方法,所以先要获取到每一个字节码文件对应的 Class 类型的对象。

获取 Class 对象三种方式
  • 方式一:调用 Object 类的 getClass():任何类都会继承此方法

    Personp=newPerson();
    Classc=p.getClass()

  • 方式二:调用某个类的class属性来获取该类对呀的Class对象

    Classs=Student.class

  • 方式三:调用 Class 类的静态方法 forName(StringclazzName),参数的值是某个类的全限 定类名(必须添加完整包名)

    Classc3=Class.forName(“com.jf.weidong.Person”);

通过这个 Class 对象,可以获取 Student 类内部的成员属性、构造方法、成员方法的一些信 息,并能够调用它们

示例代码
public class Test01 {
    public static void main(String[] args) throws ClassNotFoundException {
        // TODO Auto-generated method stub

        /*
         * 获取 Class 对象三种方式
         */
        Student stu = new Student();
        Class c1 = stu.getClass();
        System.out.println(c1);

        Class c2 = Student.class;
        System.out.println(c2);

        Class c3 = Class.forName("classtry.Student");
        System.out.println(c3);
    }
}

反射操作构造方法

通过反射获取构造方法

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

- 获取单个

Constructor getConstructor(Class … parameterTypes):获取指定的”公有构 造方法”
Constructor getDeclaredConstructor(Class … parameterTypes):获取指定的 构造方法(包括私有的)

  • 批量获取构造方法

    Constructor[] getConstructors() :获取所有的”公有构造方法”
    Constructor[] getDeclaredConstructors() :获取全部(包括私有)的构造方法

  • 暴力访问:如果想要访问私有成员,需要设置暴力访问

Constructor 的 setAccessible(true):不进行权限检查

示例代码
public class Test02 {

    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        //获取单个 
        // Constructor getConstructor(Class ... parameterTypes):获取指定的"公有构 造方法" 
        Class c = Class.forName("classtry.Student");
        Constructor con1 = c.getConstructor(String.class,int.class);
        System.out.println(con1);
        System.out.println("=============================");
        //   Constructor getDeclaredConstructor(Class ... parameterTypes):获取指定的 构造方法(包括私有的) 
        Constructor con2 = c.getDeclaredConstructor(String.class);
        System.out.println(con2);
        System.out.println("=============================");
        // 批量获取构造方法
        // Constructor[] getConstructors() :获取所有的"公有构造方法" 
        Constructor[] con3 = c.getConstructors();
        for (Constructor c1 : con3) {
            System.out.println(c1);
        }
        System.out.println("==============================");
        // Constructor[] getDeclaredConstructors() :获取全部(包括私有)的构造方法 
        Constructor[] con4 = c.getDeclaredConstructors();
        for (Constructor c2 : con4) {
            System.out.println(c2);
        }
    }

}
通过反射方式创建对象(共有构造)
  • 步骤如下
    1. 获取到 Class 对象
    2. 获取指定的构造方法
    3. 通过构造方法类 Constructor 中的方法,创建对象
      publicTnewInstance(Object…initargs)
示例代码
public class Test03 {

    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub

        //publicTnewInstance(Object...initargs) 

        Class c = Class.forName("classtry.Student");
        Constructor con1 = c.getConstructor();
        Object obj1 = con1.newInstance();
        System.out.println(obj1);
        System.out.println("=========================");
        Constructor con2 = c.getConstructor(String.class,int.class);
        Object obj2 = con2.newInstance("张三",21);
        System.out.println(obj2);
        System.out.println("=========================");
        Constructor con3 = c.getDeclaredConstructor(String.class,int.class,String.class);
        Object obj3 = con3.newInstance("李四",22,"中国");
        System.out.println(obj3);
    }

}
通过反射方式创建对象(私有构造)
AccessibleObject 类是 Field、Method 和 Constructor 对象的父类。它提供了将反射的 对象标记为在使用时取消默认 Java 语言访问控制检查的能力。 对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、 Method 或 Constructor 对象来设置或获取字段、调用方法,或者创建和初始化类的新实例 的时候,会执行访问检查。常用方法如下:
  • public void setAccessible(boolean flag) throws SecurityException

    参数值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。参数值 为 false 则指示反射的对象应该实施 Java 语言访问检查。

  • 获取私有构造方法,步骤如下:

    1. 获取到 Class 对象
    2. 获取指定的构造方法
    3. 暴力访问, 通过 setAccessible(booleanflag)方法
    4. 通过构造方法类 Constructor 中的方法,创建对象

    publicTnewInstance(Object…initargs)

示例代码
public class Test04 {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        // TODO Auto-generated method stub

        Class c = Class.forName("classtry.Student");
        Constructor con1 = c.getDeclaredConstructor(String.class);
        con1.setAccessible(true);//不加这一行会报错!!!
        Object obj1 = con1.newInstance("李四");
        System.out.println(obj1);
    }

}

反射操作属性

通过反射获取成员变量并使用

在反射机制中,把类中的成员变量使用类 Field 表示。可通过 Class 类中提供的方法获取成 员变量:

  • 获取单个成员变量

    publicFieldgetField(Stringname) 获取指定的 public 修饰的变量
    publicFieldgetDeclaredField(Stringname) 获取指定的任意变量

  • 获取多个成员变量

    publicField[]getFields() 获取所有 public 修饰的变量
    publicField[]getDeclaredFields() 获取所有的变量 (包含私有)

示例代码
public class Test05 {
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        /*
         * - 获取单个成员变量
         *  > public Field getField(Stringname) 获取指定的 public 修饰的变量 
         *  > public Field getDeclaredField(Stringname) 获取指定的任意变量
         */
        Class c = Class.forName("classtry.Student");
        Field name = c.getField("name");
        System.out.println(name);
        Field adress = c.getDeclaredField("adress");
        System.out.println(adress);
        System.out.println("==============================");
        /*- 获取多个成员变量
         *> public Field[] getFields() 获取所有 public 修饰的变量 
         *> public Field[] getDeclaredFields() 获取所有的变量 (包含私有)
         * 
         */
        Field[] f1 = c.getFields();
        for (Field ff1 : f1) {
            System.out.println(ff1);
        }
        System.out.println("==============================");

        Field[] f2 = c.getDeclaredFields();
        for (Field ff2 : f2) {
            System.out.println(ff2);
        }

    }

}
通过反射,获取指定的成员变量,并赋值

获取成员变量,步骤如下:
1. 获取 Class 对象
2. 获取构造方法
3. 通过构造方法,创建对象
4. 获取指定的成员变量(私有成员变量,通过 setAccessible(booleanflag)方法暴力访问)
5. 通过方法,给指定对象的指定成员变量赋值或者获取值

  • 方法介绍
    publicvoidset(Objectobj,Objectvalue):在指定对象 obj 中,将此 Field 对象表示的成员 变量设置为指定的新值
    publicObjectget(Objectobj):返回指定对象 obj 中,此 Field 对象表示的成员变量的值
示例代码
public class Test06 {

    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub

        /*
         * public void set(Objectobj,Objectvalue):在指定对象 obj 中,
         *          将此 Field 对象表示的成员 变量设置为指定的新值 
         * public Object get(Objectobj):返回指定对象 obj 中,此 Field 对象表示的成员变量的值
         */
        Class c = Class.forName("classtry.Student");
        Object obj = c.getConstructor().newInstance();
        Field name = c.getField("name");
        name.set(obj, "张三");
        System.out.println(obj);
        System.out.println("========================");
        Object nameString = name.get(obj);
        if(nameString instanceof String){
            System.out.println("张三");
        }else{
            System.out.println("李四");
        }   
    }

}

反射操作成员方法

在反射机制中,把类中的成员方法使用类 Method 表示。可通过 Class 类中提供的方法获取 成员

- 获取多个

Method[]getMethods():获取所有”公有方法”(包括继承的)
Method[]getDeclaredMethods():获取所有成员方法(包括私有)

- 获取单个成员方法

MethodgetMethod(Stringname,Class…parameterTypes):获取指定的”公有方法”
MethodgetDeclaredMethod(Stringname,Class…parameterTypes):获取指定的方法,包括 私有的

  • 调用方法:(注意:1.要先创建此类的对象;2.调用私有方法前,要先设置暴力访问)

Method
Objectinvoke(Objectobj,Object…args):调用 Method 所代表的方法
返回值:此 Method 对象调用所代表的方法,所获取的返回值;
形参:
obj:方法所属对象;
args:Method 所代表的那个方法的实参列表

示例代码
public class Test07 {
/**
 * 反射操作成员方法
 * @param args
 * @throws Exception 
 */
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        /*
         *  获取多个
         *   Method[]getMethods():获取所有"公有方法"(包括继承的) 
         *   Method[]getDeclaredMethods():获取所有成员方法(包括私有) 
         */  
         Class c = Class.forName("classtry.Student");
         Object obj = c.getConstructor().newInstance();
         Constructor con1 = c.getDeclaredConstructor();
//       con1.setAccessible(true);
         Object obj2 = con1.newInstance();
         Method[] m1 = c.getMethods();
         for (Method method1 : m1) {
            System.out.println(method1);
         }
         System.out.println("===============================");
         Method[] m2 = c.getDeclaredMethods();
         for (Method method2 : m2) {
            System.out.println(method2);
         }
         System.out.println("===============================");
        /*
         * 获取单个成员方法
         * MethodgetMethod(Stringname,Class...parameterTypes):获取指定的"公有方法"
         * MethodgetDeclaredMethod(Stringname,Class...parameterTypes):
         * 获取指定的方法,包括 私有的
         */
         Method m3 = c.getMethod("eat");
         m3.invoke(obj);
         Method m4 = c.getMethod("num",int.class);
         m4.invoke(obj, 3);
         Method m5 = c.getMethod("nums", int.class);
         Object result = m5.invoke(obj, 1);
         System.out.println(result);

         System.out.println("===============================");

         Method m6 = c.getDeclaredMethod("learn",null);
         m6.setAccessible(true);
         m6.invoke(obj2);


    }

}

通过反射创建对象,并调用指定的方法

执行找到的方法

publicObjectinvoke(Objectobj, Object…args)
执行指定对象 obj 中,当前 Method 对象所代表的方法,方法要传入的参数通过 args 指定。

示例代码
public class Test08 {
/**
 * 通过反射创建对象,并调用指定的方法
 * @param args
 * @throws Exception 
 */
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub

        /*执行找到的方法 
         * public Object invoke(Objectobj, Object...args) 
         * 执行指定对象 obj 中,当前 Method 对象所代表的方法,方法要传入的参数通过 args 指定。
         */
        Class c = Class.forName("classtry.Student");
        Constructor con = c.getDeclaredConstructor();
//      con.setAccessible(true);
        Object obj = con.newInstance();
        Method m1 = c.getMethod("eat", null);
        m1.invoke(obj);
        Method m2 = c.getDeclaredMethod("study", null);
        m2.setAccessible(true);
        m2.invoke(obj);

    }

}

反射练习

反射运行配置文件内容

  • Properties 介绍
    Properties 继承自 Hashtable,说明其是以键与值的方式存在的,只是 Properties 的 键与值都是必须是字符串类型的数据。Properties 常被用于配置文件的写入与读取, 配置文件中记录着程序的各项参数信息,使用程序的用户可以自定义某些参数,以达到 软件的个性化。

  • 通过反射读取配置文件,可以不改变主程序代码,只要修改配置文件就可以操作不同的 对象执行不同的功能。

示例代码
public class Demo {

    public static void main(String[] args) throws Exception{
        // TODO Auto-generated method stub
//      System.out.println(Test02.class);
        Class c = Class.forName(porties("className"));
        Object obj = c.getConstructor().newInstance();
        Method m1 = c.getMethod(porties("methodName"));
        m1.invoke(obj);

    }
    public static String porties(String key) throws Exception{
        Properties p = new Properties();
        FileReader fr = new FileReader("protiesty.properties");
        p.load(fr);
        fr.close();
        return p.getProperty(key);
    }

}
public class Test01 {

    public void show1(){
        System.out.println("这是方法show1");
    }
}
public class Test02 {

    public void show2(){
        System.out.println("这是方法show2");
    }
}
//配置文件中
className = classtrain.Test02
methodName = show2

通过反射越过泛型类型检查

通过反射越过泛型检查,定义一个具有 String 泛型的集合,要求成功向集合中添加一个 int 数据

示例代码
public class Demo2 {

    public static void main(String[] args) throws Exception, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
        // TODO Auto-generated method stub
        List<String> list = new ArrayList<>();
        list.add("zhangsan");
        list.add("lisi");
        //list.add(12345);//该行报错
        Class c = list.getClass();
        //Object obj = c.getConstructor().newInstance();
        Method m1 = c.getMethod("add", Object.class);
        m1.invoke(list, 12435);
        for (Object str : list) {
            System.out.println(str);
        }
    }

}

通用设置属性的方法

通过反射写一个通用的方法,能够设置某个对象的某个属性为指定的值

示例代码
public class Demo3 {

    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        Cat c = new Cat();
        usual(c, "name", "123456");
        c.show();

    }
    public static void usual(Object kindName,String filedName,Object value) throws Exception{
        Class c = kindName.getClass();
        Field fname = c.getDeclaredField(filedName);
        fname.setAccessible(true);
        fname.set(kindName, value);
    }

}
public class Cat {

    private String name;
    public void show() {
        // TODO Auto-generated method stub

        System.out.println("瞎、。。。叫"+this.name);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值