java反射基础知识与代码

类加载器和反射

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

  • 加载

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

    • 验证 是否有正确的内部结构,并和其他类协调一致
    • 准备 负责为类的静态成员分配内存,并设置默认初始化值
    • 解析 将类的二进制数据中的符号引用替换为直接引用
    • 初始化 就是我们以前讲过得初始化步骤
类初始化时机
  • 创建类的实例
  • 访问类的静态变量,或者为静态变量赋值
  • 调用类的静态方法
  • 使用反射方式来强制创建某个类或者接口对应的java.lang.Class对象
  • 初始化某个类的子类
  • 直接使用java.exe命令来运行某个主类
类加载器
  • 类加载器
  • 负责将.class文件加载到内存中,并为之生成对应的Class对象。
  • 虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。
  • 类架子啊器的组成
  • Bootstrap ClassLoader 根类加载器
  • Extension ClassLoader 扩展类加载器
  • System ClassLoader 系统类加载器
类加载器的作用
  • Bootstrap ClassLoader 根类加载器
  • 也被称为引导类加载器,负责Java核心类的加载
    • 比如System,String等。在JDK中JRE的lib目录下rt.jar文件中。
  • Extension ClassLoader扩展类加载器
  • 负责JRE的扩展目录中jar包的加载
    • 在JDK中JRE的lib目录下ext目录
  • System ClassLoader 系统类加载器
  • 负责在JVM启动时加载来自java命令的class文件,以及classPath环境变量所指定的jar包和类路径
反射
  • JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能构调用他的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

  • 简单的讲,反射就是指我现在只有class文件 ,然后你拿到class文件去使用成员变量,构造方法 ,成员方法。 不是拿着java文件而是class文件,反着去用它

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

如何获取Class文件类对象
/*
* 反射:就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。
*
* 要想这样使用,首先你必须得到class文件对象,其实就是得到Class类的对象。所有的.class文件都是Class类的对象
*
* Class类:
*       Class类中可以的到下面几个类
*       分别把这三个东西定义成类,通过这几个类的对象去调方法,使用该方法,这叫反射
*       成员变量    Field
*       构造方法    Constructor
*       成员方法    Method
* Constructor :提供关于类的单个构造方法的信息以及对它的访问权限。
*
* 获取class文件对象的方式:
* A:Object类的getClass()方法
* B:数据类型的静态属性class
* C:Class类中的静态方法
*   public static Class forName(Stirng className)
*
* 一般我们到底使用谁呢?
*       A:自己玩  任选一种
*       B:开发    第三种
*           为什么呢?因为第三种是一个字符串,而不是一个具体的类名。这样我们就可以把这样的字符串配置到文件中。
* */
public class ReflectDemo {
   public static void main(String[] args) throws ClassNotFoundException {
       // 方式1
       Person p = new Person();
       Class c = p.getClass(); // 运行期间对象c拿到了对象p的class文件对象
       Person p2 = new Person();
       Class c2 = p2.getClass();
       System.out.println(p == p2); // false
       /*
       * 这里为什么是等于呢,因为这里这里获取的
       * 是同一个类的字节码文件对象,只有一个字节码文件对象,所以是true
       * */
       System.out.println(c == c2); // true

       // 方式2
       Class c3 = Person.class;
       System.out.println(c == c3);

       // 方式3
       // ClassNotFoundException
       Class c4 = Class.forName("cn.itcast_01.Person");    // 这里的参数是获取该类的全路径名称
       System.out.println(c == c4);

   }
}
如果通过反射获取构造方法
package cn.itcast_02;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/*
* 通过反射获取构造方法并使用。
* */
public class ReflectDemo {
   public static void main(String[] args) throws Exception{
        // 获取字节码文件对象
       Class c = Class.forName("cn.itcast_01.Person");

       // 获取所有的公共的构造方法
       // public Constructor[] getConstructors():所有公共构造方法
       // public Constructor[] getDeclaredConstructors():所有构造方法
//        Constructor[] cons = c.getDeclaredConstructors();
//        // 遍历获取的构造方法打印输出路径
//        for(Constructor con: cons){
//            System.out.println(con);

       // 获取单个构造方法                         下面的...叫可变参数,可有可无,可多,可少
       // public Constructor[] getConstructor(Class<?>... ParameterTypes):     获取指定的公共的构造方法
       // 参数表示的是:你要获取的构造法方法的构造参数个数及数据类型的class字节码文件对象
       Constructor con = c.getConstructor();       // 这里返回的是构造方法对象

       // Person p = new Person();
       // public T newInstance(Object... intargs)

       // 使用此Constructor 对象表示的构造方法来创建该构造方法的声明类的实例,并用指定的初始化参数初始化该实例。
       Object obj = con.newInstance();
       System.out.println(obj);
       }
   }
如何使用反射来创建对象
public class ReflectDemo{
   public void main(Stirng[] args){
       // 获取字字节码文件对象
       Class c = Class.forName("cn.itcast_01.Person");
       // 通过字节码文件对象获取构造方法
       Constructor con = c.getConstructor();
       // 通过反射来创建对象
       Object obj2 = con.newInstance();
       // 把创建好的对象强制转换为Person就可以调Person的方法了
       Person p = (Person)obj2;
       p.show();
   }
}
通过反射获取带参构造方法,创建对象
import java.lang.reflect.Constructor;

/*
* 需求:通过反射区获取该构造方法并使用:
* public Person(String name,int age,String address)
*
*
* */
public class RefilectDemo02 {
   public static void main(String[] args) throws Exception {
       //获取字节码文件对象
       Class c1 = Class.forName("cn.itcast_01.Person");
       // 通过字节码文件对象获取带参构造方法对象  参数的意思是获取同等参数的构造方法,但是参数得夹class
       Constructor con = c1.getConstructor(String.class, int.class, String.class);

       // 通过带参构造方法对象创建对象
       // public T newInstance(Object... initargs)
       Object obj = con.newInstance("林青霞",27,"北京");
       System.out.println(obj);
   }
}
通过反射获取私有构造方法
import java.lang.reflect.Constructor;

/*
* 需求:通过反射获取私有构造方法并使用
* */
public class RefilectDemo03 {
   public static void main(String[] args) throws Exception {
       // 获取字节码文件对象
       Class c = Class.forName("cn.itcast_01.Person");

       // 获取私有构造方法
       // NoSuchMethodException:没这个方法异常
       // 原因是一开始我们使用的方法只能获取公共的,下面这种方式就可以了。
       //Constructor con = c.getConstructor(String.class);
       Constructor con= c.getDeclaredConstructor(String.class);

       // 用该私有构造方法创建对象
       // IllegalAccessException:非法的访问异常。
       // 暴力访问
       con.setAccessible(true); // 值为true则指示反射的对象在使用时应该取消Java语言访 问检查。
       /*
       * 这样就不安全了吗,可以在写class文件的时候把calss文件进行一个加密,每个class可以多写几句话,这样就不能通过反射进行还原了也可以通过异或某个字符,写进去,读的时候在异或一下
       * */
       Object obj = con.newInstance("风清扬");
       System.out.println(obj);
   }
}
通过反射获取成员变量
package cn.itcast_03;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

/*
* 通过反射获取成员变量并使用
* */
public class ReflectDemo {
   public static void main(String[] args) throws Exception {
       // 获取字节码文件对象
       Class c = Class.forName("cn.itcast_01.Person");
       // 获取所有的公共成员变量
       //Field[] fields = c.getFields();
       // 获取所有的成员变量
//        Field[] fields = c.getDeclaredFields();
//        for(Field field :fields){
//            System.out.println(field);
//
//        }
       // 通过无参构造方法创建对象
       Constructor con = c.getConstructor();
       Object obj = con.newInstance();
       // 获取单个的成员变量
       // 获取address并对其赋值
       Field addressField = c.getField("address");
       // public void set(Object obj,Object value)
       // 将指定对象变量上此Field 对象表示的字段设置文指定的新值。
       /*
       * 其实是给这个个obj对象的addressField属性设置为北京
       * */
       addressField.set(obj,"北京"); // 给obj对象的addressField字段设置值为北京
       System.out.println(obj);
       // 获取name并对其赋值
       Field f = c.getDeclaredField("name");
       f.setAccessible(true);           // 设置可访问私有的
       f.set(obj,"张三");
       System.out.println(obj);

   }
}
通过反射获取构造方法进行创建对象来使用方法
package cn.itcast_03;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class ReflectDemo02 {
   public static void main(String[] args) throws Exception {
       // 获取字节码文件对象
       Class c = Class.forName("cn.itcast_01.Person");

       // 获取所有公共的方法
       // Method[] methods = c.getMethods();
       // Method[] methods = c.getMethods();
       // 获取所有的方法
//        Method[] methods = c.getDeclaredMethods();
//        for(Method method :methods){
//            System.out.println(method);
//        }

       // 调用位五参数方法
       // 通过字节文件对象获取构造方法对象
       Constructor con = c.getConstructor();
       // 通过构造方法创建使用newInstance方法创建实例
       Object obj = con.newInstance();
       // 通过字节码文件对象获取show方法
       Method m1 = c.getMethod("show");
       // 使用字节码文件创建出来的show方法对象,调用invoke方法,传入一个show方法所在类的对象,进行使用show方法
       m1.invoke(obj);

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

       // 调用带参方法
       // public void method(Stirng s)
       // 通过字节码文件对象获取指定指定方法名称,类型,相同的方法
       Method m2 = c.getMethod("method",String.class);
       // 通过字节码文件对象拿到的方法对象,使用obj方法进行调用,传入参数“hello”进行使用
       m2.invoke(obj,"hello");
       System.out.println("--------");
       Method m3 = c.getMethod("getString",String.class,int.class);
       // Object objString = m3.invoke(obj,"hello",100);
       String s = (String) m3.invoke(obj,"hello",100);
       System.out.println(s);
       
       // 调用私有方法
       // private void function()
       Method m4 = c.getDeclaredMethod("function");
       // 打开java获取私有m4私有方法的机制
       m4.setAccessible(true);
       // 使用obj队形啊调用私有方法m4
       m4.invoke(obj);
   }
}

反射练习题
习题1:通过配置we年运行类中的方法

class.txt文件内容

className=cn.itcast_04.Teacher				#这里表示需要获取的类的全路径名称
methodName=love								#这里表示需要使用获取类的指定方法

Teacher.java文件内容

package cn.itcast_04;

public class Teacher {
   public void love(){
       System.out.println("爱生活,爱讲课");
   }
}

Student.java文件的内容

package cn.itcast_04;

public class Student {
   public void love(){
       System.out.println("爱生活,爱java");
   }
}

Test.java主方法所在类的内容

package cn.itcast_04;

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

/*
* 通过配置文件运行类中的方法
* 反射:
*       需要配置文件配合使用。
*       使用class.txt代替
*       并且你知道有两个键。
*           className
*           methodName
* */
public class Test {
   public static void main(String[] args) throws Exception {
       // 反射钱的做法,不好
       /*
       Student s = new Student();
       s.love();
       Teacher t = new Teacher();
       s.love();
       */
       // 反射后的做法
       // 加载键值对数据
       Properties prop = new Properties();
       // 创建一个初入流进行读取查class.txt里面的数据
       FileReader fr = new FileReader("D:\\JavaSE\\Java_Fanshe\\java_Fanshe\\class.txt");
       // 使用prop调用load方法以简单的线性格式从字符流读取属性列表(关键字和元素)
       prop.load(fr);
       // 挂你资源
       fr.close();

       // 获取数据,获取clas.txt文件中的键对应的值
       String className = prop.getProperty("className");       // 获取需要的类的全路径名称,用去获取字节码码文件对象
       String methodName = prop.getProperty("methodName");     // 获取需要的调用的指定的方法

       // 反射
       Class c = Class.forName(className);
       // 通过获取到的字节码文件对象获取无偿构造
       Constructor con = c.getConstructor();
       // 使用反射获取到的无参构造方法进行创建实例(对象)
       Object obj = con.newInstance();

       // 调用方法
       Method m = c.getMethod(methodName);
       // 使用invoke方法,通过obj来调用m方法进行使用
        m.invoke(obj);
   }
}
习题2:我给你ArrayList<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?
package cn.itcast_04;

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

/*
* 我给你Array<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现?
* */
public class ArrayListDemo {
   public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
       // 创建集合对象
       ArrayList<Integer> array = new ArrayList<Integer>();

       // 想要跨过泛型机制,就要使用反射进行跨越
       // 获取集合array对象的class文件对象
       Class c = array.getClass();
       // 使用获取的集合的字节码文件对象来获取array集合的add方法对象
       Method m = c.getMethod("add", Object.class);
       // 调用array的add方法,传入值是helleo
       m.invoke(array,"hello");

       System.out.println(array);
   }
}
习题3:写一个方法,public void setProperty(Object obj,String propertyName,Object value){},此方法可以将obj对象中名为propertyName的属性的值设为value。

Tool.java工具类

package cn.itcast_04;

import java.lang.reflect.Field;

public class Tool {
   public void setProperty(Object obj,String propertyName,Object value) throws NoSuchFieldException, IllegalAccessException {
       // 根据对象获取字节码文件对象
       Class c = obj.getClass();
       // 获取该对象的propertyName成员变量
       Field field = c.getDeclaredField(propertyName);
       // 取消访问检查
       field.setAccessible(true);
       // 给对象的成员变量赋值为指定的值
       field.set(obj,value);

   }
}

ToolDemo.java测试类

package cn.itcast_04;

public class ToolDemo {
   public static void mai n(String[] args) throws NoSuchFieldException, IllegalAccessException {
       Person p = new Person();
       Tool t = new Tool();
       t.setProperty(p,"name","张三");
       t.setProperty(p,"age",26);
       System.out.println(p);
   }
}
class Person{
   private String name;
   public int age;

   @Override
   public String toString() {
       return
              name + '\t'+ age;
   }
}
动态代理
动态代理
  • 代理,本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。

  • 举例:春季回家买票让人代买

  • 动态代理:在程序运行过程中产生的这个对象

  • 而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态dialing其实就是通过反射来生成一个代理

  • 在java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象,JDK提供的动态代理只能针对接口做代理。我们有更强大的代理cglib

  • Proxy类中的方法创建动态代理类对象

  • public stataic Object newProxyInstance(ClassLoaderloader.Class<?>[] interfaces.InvocationHandler h)

  • 最终会调用InvocationHandler的方法

  • InvocationHandler

  • Object invoke(Object proxy,Method method.Object[] args)

Proxy类中创建动态代理对象的方法的三个参数;

ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载

Interface独享的数组,表示的是我将要给我需要代理的对象提供了一组什么接口,如果我提供了一组接口给他,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值