Java反射01

类加载器

类的加载

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

加载

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

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

连接

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

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

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

初始化

就是我们以前讲过的初始化步骤

类的初始化时机

创建类的实例

访问类的静态变量,或者为静态变量赋值

调用类的静态方法

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

初始化某个类的子类

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

类加载器

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

虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行

类加载器的组成

Bootstrap ClassLoader 根类加载器

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

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

Extension ClassLoader 扩展类加载器

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

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

System ClassLoader 系统类加载器

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

通过这些描述我们就可以知道我们常用的东西的加载都是由谁来完成的

到目前为止我们已经知道把class文件加载到内存了,那么,如果我们仅仅站在这些class文件的角度,我们该如何使用这些class文件的内容呢?

这就是反射所要研究的内容了

反射

概述

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

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

简单概括:

就是通过class文件对象去使用该文件中的成员变量,构造方法,成员方法

之前写的很多类java文件,再去实例化的时候,直接new就可以了

但是现在并没有现成的java文件,只拿到一个class文件怎么办呢?

换句话说,就算你是用java文件去创建对象,底层依旧是需要一个class文件

也就是说想要创建对象,就必须要得到一个class文件,也就是获取该class的文件对象

获取到class文件对象

1、通过Object中的getClass()方法获取,返回该Object的运行时类

2、通过类名获取静态属性class

3、Class类中的静态方法

public static 类<?> forName(String className)

返回与给定字符串名称的类或接口相关联的类对象

注意:这里需要写该类在本项目中完整路径,且\\用.代替,如

Class<?> person = Class.forName("com.bigdata.java.day02.day26.Person");

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

获取构造方法

public Constructor<?>[] getConstructors()

返回一个包含Constructor对象的数组。反映由此Constructor对象表示的类的所有公共类函数,不包括获取私有的,默认的修饰构造方法

public Constructor<?>[] getDeclaredConstructors()

返回反映Constructor对象表示的类声明的所有Constructor对象的数组类

获取所有的构造方法,包括私有,该方法打破了原先私有构造方法不能创建对象的隔阂

public Constructor<T> getConstructor(Class<?>... parameterTypes)

返回一个Constructor对象,该对象反映Constructor对象表示的类的指定的公共类函数

public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

返回一个Constructor对象,该对象反映Constructor对象表示的类或接口的指定类函数

创建对象

public T newInstance(Object... initargs)

获取构造方法的目的是创建对象

对象表示的构造函数,使用指定的初始化参数创建和初始化构造函数的声明类的新实例

con.newInstance("zhangsan",20);

public class Test {
    public static void main(String[] args) throws Exception{
        Class<?> person = Class.forName("com.bigdata.java.day02.day26.Person");
​
        //获取构造方法数组
        Constructor<?>[] pc01 = person.getConstructors();
        for (Constructor<?> constructor : pc01) {
            System.out.println(constructor);
        }
        System.out.println("----------------------------");
​
        Constructor<?>[] pc02 = person.getDeclaredConstructors();
        for (Constructor<?> constructor : pc02) {
            System.out.println(constructor);
        }
        System.out.println("----------------------------");
​
        //获取单个构造方法
        Constructor<?> c1 = person.getConstructor(String.class,int.class);
        Object p1 = c1.newInstance("大7", 18);
        System.out.println(p1);
        System.out.println("----------------------------");
​
        Constructor<?> c2 = person.getDeclaredConstructor();
        c2.setAccessible(true);
        Object p2 = c2.newInstance();
        System.out.println(p2);
        System.out.println("----------------------------");
    }
}
​
//结果
public com.bigdata.java.day02.day26.Person(java.lang.String,int)
----------------------------
public com.bigdata.java.day02.day26.Person(java.lang.String,int)
com.bigdata.java.day02.day26.Person(java.lang.String)
private com.bigdata.java.day02.day26.Person()
----------------------------
Person{name='大7', age=18, sex='null'}
----------------------------
Person{name='null', age=0, sex='null'}
----------------------------

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

获取所有成员

public Field[] getFields()

获取所有的成员变量

返回包含一个数组Field对象反射由此表示的类或接口的所有可访问的公共字段类对象

获取该类中所有公共的成员变量

public Field[] getDeclaredFields()

返回的数组Field对象反映此表示的类或接口声明的所有字段类对象

获取所有的成员变量,包括公共,受保护,默认(包)访问和私有字段

获取单个成员

public Field getField(String name)

返回一个Field对象,它反映此表示的类或接口的指定公共成员字段类对象,name是字段名称

public Field getDeclaredField(String name)

返回一个Field对象,它反映此表示的类或接口的指定已声明字段类对象

修改成员的值

void set(Object obj,Object value)

将指定对象变量上此Field对象表示的字段设置为指定的新值

public class Test02  {
    public static void main(String[] args) throws Exception{
        Class<?> pc = Class.forName("com.bigdata.java.day02.day26.Person");
​
        Constructor<?> p1 = pc.getDeclaredConstructor(String.class, int.class);
        Object o = p1.newInstance("大7", 18);
        Person person=(Person)o;
​
        //获取成员变量组
        Field[] f01 = pc.getFields();
        for (Field field : f01) {
            System.out.println(field);
        }
        System.out.println("--------------------------");
​
        Field[] f02 = pc.getDeclaredFields();
        for (Field field : f02) {
            System.out.println(field);
        }
        System.out.println("--------------------------");
​
        //获取单个成员变量
        Field sex = pc.getField("sex");
        //给这个变量赋值
        sex.set(person,"男");
        System.out.println(person);
        System.out.println("----------------------------");
​
        //获取单个成员变量(私有)
        Field name = pc.getDeclaredField("name");
        //暴力访问
        name.setAccessible(true);
        //给这个变量赋值
        name.set(person,"大大7");
        System.out.println(person);
        System.out.println("----------------------------");
    }
}
​
//结果
public java.lang.String com.bigdata.java.day02.day26.Person.sex
--------------------------
private java.lang.String com.bigdata.java.day02.day26.Person.name
int com.bigdata.java.day02.day26.Person.age
public java.lang.String com.bigdata.java.day02.day26.Person.sex
--------------------------
Person{name='大7', age=18, sex='男'}
----------------------------
Person{name='大大7', age=18, sex='男'}
----------------------------

通过反射获取成员方法并使用

获取所有方法

public Method[] getMethods()

返回包含一个数组方法对象反射由此表示的类或接口的所有公共方法类对象

包括哪些由类或接口和那些从超类和超借口继承的声明

获取的是所有公共的方法,不光获取的是自己的公共方法,还包括父类的

public Method[] getDeclaredMethods()

返回包含一个数组方法对象反射的类或接口的所有声明的方法

包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法

只能获取自己的所有方法,包括私有的

获取单个方法

public Method getMethod(String name,class<?>... parameterTypes)

返回一个方法对象,它反映此表示的类或接口的指定公共成员方法类对象

注意:只写方法名,不写小括号,若有参数后面跟参数类型的.class

public Method getDeclaredMethod(String name,class<?>... parameterTypes)

返回一个方法对象,它反映此表示的类或接口的指定公共成员方法类对象

运行方法

Object invoke(Object obj,Object... args)

在具有指定参数的方法对象上调用此方法对象表示的底层方法

暴力访问

method.setAccessible(true);

public class Test03 {
    public static void main(String[] args) throws Exception{
        Class<?> pc = Class.forName("com.bigdata.java.day02.day26.Person");
​
        Constructor<?> constructor = pc.getConstructor(String.class, int.class);
        Object o = constructor.newInstance("大7", 18);
        Person person = (Person)o;
​
        //获取所有方法
        Method[] m01 = pc.getMethods();
        for (Method method : m01) {
            System.out.println(method);
        }
        System.out.println("-----------------------");
​
        Method[] m02 = pc.getDeclaredMethods();
        for (Method method : m02) {
            System.out.println(method);
        }
        System.out.println("-----------------------");
​
        //获取单个方法
        Method fun01 = pc.getMethod("fun", String.class, int.class);
        fun01.invoke(person,"dada",16);
        System.out.println("-----------------------");
​
        Method fun02 = pc.getDeclaredMethod("fun");
        fun02.setAccessible(true);
        fun02.invoke(person);
    }
}
​
//结果
public java.lang.String com.bigdata.java.day02.day26.Person.toString()
public void com.bigdata.java.day02.day26.Person.fun(java.lang.String,int)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) 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.bigdata.java.day02.day26.Person.toString()
void com.bigdata.java.day02.day26.Person.fun(java.lang.String)
public void com.bigdata.java.day02.day26.Person.fun(java.lang.String,int)
private void com.bigdata.java.day02.day26.Person.fun()
-----------------------
这是公共的方法
-----------------------
这是私有的方法

通过配置文件和反射来获取类的成员

学习反射之后,java里面提供了一个配置文件类

Properties

config.txt文件
    
className=com.shujia.java.Student
methodName=study
Properties properties=new Properties();
FileReader fileReader=new FileReader("config文件的路径");
properties.load(fileReader);            //载入配置文件
fileReader.close();
​
//获取配置文件的属性数据
String className=properties.getProperty("className");
System.out.println(className);
String methodName=properties.getProperty("methodName");
​
//通过反射获取class文件对象 
Class<?> c=Class.forName(className);
Constructor<?> cons=c.getContructor();
Object o=cons.newInstance();
​
//通过反射调用方法
Method mothod=c.getMethod("methodName");
method.invoke(o);

案例

绕过泛型指定的数据类型机制,添加数据

//我给你ArrayList<Integer>的一个对象
//我想在这个集合中添加一个字符串数据,通过反射实现
​
ArrayList<Integer> list=new ArrayList<>();
​
Class<? extends ArrayList> c=list.getClass();
Method method=c.getMethod("add",Object.class);
method.incoke(list,"hello");

动态代理

概述

代理:本来应该自己做的事情,却清了别人来做,被请的人就是代理对象

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

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

实现

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

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

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

最终会调用InvocationHandler的方法

InvocationHander

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

public interface Person {
    public abstract void eat();
    public abstract  void sleep();
}
​
public class PersonDemo implements Person{
    @Override
    public void eat() {
        System.out.println("我在吃饭");
    }
​
    @Override
    public void sleep() {
        System.out.println("我在睡觉");
    }
}
​
public class HandleDemo implements InvocationHandler {
    private Object object;
​
    public HandleDemo(Object object) {
        this.object = object;
    }
​
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("检查日志");
        method.invoke(this.object);
        System.out.println("打印日志");
​
        return null;
    }
}
public class Demo01 {
    public static void main(String[] args) throws Exception {
​
        PersonDemo personDemo = new PersonDemo();
​
        Object o = Proxy.newProxyInstance(personDemo.getClass().getClassLoader()
                , personDemo.getClass().getInterfaces()
                , new HandleDemo(personDemo));
​
        Person p=(Person)o;
        p.eat();
        p.sleep();
​
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值