【黑马程序员】反射

c——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

反射

类的加载及类加载器

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

加载:
将class文件读入内存,并为之建立一个Class对象
任何类被使用时,系统都会建立一个Class对象

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

初始化:按顺序进行初始化

类的加载时机:

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

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

类加载器的组成:
Bootstrap ClassLoader 根类加载器(负责核心类的加载)
Extension ClassLoader 扩展类加载器(负责扩展目录下的jar包加载)
System ClassLoader 系统类加载器(负责在JVM启动时,加载来自java命令的class文件和classpath环境变量指定的jar包and类路径)

反射

在运行状态中对任意类都能知道该类的所有属性和方法,对于任意对象,都能调用他的任意方法和属性.这种功能就叫反射

实现方式:先要获取该类的字节码文件对象,然后通过它去使用成员变量,构造方法,成员方法

获取class文件对象方法:

  • Object类中的getClass方法
  • 数据类型的静态属性class
  • Class类中的静态方法 Class forName(String className) 这里要写类的全路径

实际开发中常用方法C

反射的使用

直接上代码+注释

package javareflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
 * 代码演示用需要用到的Person类
 */
public class Person {
    //注意这里的修饰符
    private String name;
    int age;
    public String address;

    //注意这里没有public
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private Person(String name) {
        this.name = name;
    }

    public Person(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public Person() {
    }

    public void show(){
        System.out.println("show");
    }

    public void method(String s){
        System.out.println("method"+s);
    }

    public String getString(String s,int i){
        return s+":"+i;
    }

    private void function(){
        System.out.println("function");
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                '}';
    }
}
/**
 * Created by mo on 15/11/21.
 * 反射:通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法
 *
 *  首先要先得到class文件对象即Class类对象
 *  Class类:
 *          成员变量 Field
 *          构造方法 Constructor
 *          成员方法 Method
 *  再通过上面三个类的对象来调用
 *
 *  获取class文件对象方法:
 *      A:Object类中的getClass方法
 *      B:数据类型的静态属性class
 *      C:Class类中的静态方法 Class forName(String className) 这里要写类的全路径 会抛出ClassNotFoundException
 *      实际开发中常用方法C
 */
public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        //方法A Object类中的getClass方法
        Person p = new Person();
        Class c = p.getClass();//获取class文件对象

//        Person p2 = new Person();
//        Class c2 = p2.getClass();
//
//        System.out.println(p == p2); //false 对象是不同的
//        System.out.println(c == c2); //true 字节码是一样的

        //方法B 数据类型的静态属性class
        Class c3 = Person.class;

        //方法C Class类中的静态方法 forName(这里要传入类的全路径)
//        Class c4 = Class.forName("Person"); //报错
        Class c4 = Class.forName("javareflect.Person");//我这里Person在javareflect包下



        //通过反射获取构造方法并使用
        Constructor[] constructor = c4.getConstructors(); //获取公共构造方法
        //遍历
        for (Constructor cons:constructor){
            System.out.println(cons);
        }
        /*
        输出结果如下
        public javareflect.Person()
        public javareflect.Person(java.lang.String,int,java.lang.String)
        为什么只输出了2个构造?因为未获取到的那个构造没有用public修饰
         */
        System.out.println("----------------------");


        //获取所有构造方法,包括私有
        Constructor[] allConstructor = c4.getDeclaredConstructors();
        for (Constructor cons:allConstructor){
            System.out.println(cons);
        }
        // 输出结果比上面多了两行: javareflect.Person(java.lang.String,int)    private javareflect.Person(java.lang.String)
        System.out.println("----------------------");


        //获取单个构造 getConstructor(Class<?>... parameterTypes)
        //参数表示的是你要获取的构造方法的参数个数及数据类型的class字节码文件对象

        //获取无参构造
        Constructor constructor1 = c4.getConstructor();

        //获取带参构造 参数列表不能错,参数顺序也不能错
        Constructor constructor2 = c4.getConstructor(String.class,int.class,String.class);
        //通过Constructor对象创建Person类的实例
        Object person1 = constructor2.newInstance("mo",18,"北京");
        System.out.println(person1);

        //获取私有构造方法并使用
        Constructor constructor3 = c4.getDeclaredConstructor(String.class);
        constructor3.setAccessible(true);//值为true表示反射的对象在使用时取消Java语言访问检查
        Object person2 = constructor3.newInstance("xi");
        System.out.println(person2);
        System.out.println("----------------------");


        //通过反射获取成员变量并使用
        //获取所有成员变量
        Field[] fields = c4.getDeclaredFields(); //获取公共的成员变量用getFields()方法
        for (Field x : fields){
            System.out.println(x);
        }
        System.out.println("----------------------");

        //获取单个成员变量 并赋值
        Field nameField = c4.getDeclaredField("name");//公共成员变量用getField
        nameField.setAccessible(true);
        //赋值 String类型用set(Object obj,Object value) 给obj对象的name变量赋值为value
        //其他类型用setLong() setInt()等等
        Object p1 = constructor1.newInstance();//先创建Obj对象,通过之前获取的无参构造
        nameField.set(p1,"xixi");
        System.out.println(p1);
        System.out.println("----------------------");


        //通过反射获取成员方法并使用
        Method[] methods = c4.getDeclaredMethods(); //getMethods()会获取到自己和父类的公共方法  getDeclaredMethods()只获取自己的方法
        for (Method m:methods){
            System.out.println(m);
        }
        System.out.println("----------------------");

        //获取单个方法  Method getMethod(String name,Class<?>... parameterTypes) 参数name用于指定所需方法的简称
        Method methodShow = c4.getDeclaredMethod("show");
        //Object invoke(Object obj,Object... args) 调用方法  返回值Object  obj-调用方法的对象 args-方法调用的参数
        methodShow.invoke(p1);
        System.out.println("----------------------");

        Method methodFunc = c4.getDeclaredMethod("function");
        methodFunc.setAccessible(true);
        methodFunc.invoke(p1);
        System.out.println("----------------------");

        Method methodGet = c4.getDeclaredMethod("getString",String.class,int.class);
        Object objStr = methodGet.invoke(p1,"abc",1); //注意这里是拿Object接收,因为返回值是Object
        System.out.println(objStr);
        System.out.println("----------------------");
    }
}

反射案例

案例1:通过反射运行配置文件的内容

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

/**
 * Created by mo on 15/11/22.
 * 反射应用
 *      通过配置文件运行类中的方法
 *          需要配置文件配合使用 这里用settings.txt
 *          里面有两个键className    methodName
 *
 * 需要解决的问题:日常中经常有对类进行改动,如果不配置文件,每改一次代码就大规模改一次
 */
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        //加载配置文件中的键值对
        Properties prop = new Properties();
        FileReader fr = new FileReader("/Users/mo/IdeaProjects/hello world/JavaPractice/src/javareflect/settings.txt");
        prop.load(fr);
        fr.close();

        //获取数据
        String className = prop.getProperty("className");
        String methodName = prop.getProperty("methodName");

        //反射
        Class c = Class.forName(className);

        Constructor cons = c.getDeclaredConstructor();
        Object obj = cons.newInstance();//创建对象

        Method m = c.getDeclaredMethod(methodName);
        m.invoke(obj);
        /*
        这样 需要用哪个类哪个方法 就在配置文件里修改就行
         */
    }
}

案例2:通过反射越过泛型检查

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

/**
 * Created by mo on 15/11/22.
 * 反射越过泛型检查
 *
 * 往一个已知的ArrayList<Integer> 对象中添加一个字符串
 * 注意:泛型只是在编译的时候检查,在底层原码中仍然还是默认Object类型
 */
public class ReflectTest2 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        ArrayList<Integer> arr = new ArrayList<>();

        Class c = arr.getClass();
        Method m = c.getMethod("add",Object.class);

        m.invoke(arr,"hello");//调集合的add方法

        System.out.println(arr);
    }
}

案例3:通过反射给任意的一个对象的任意的属性赋值为指定的值

import java.lang.reflect.Field;

/**
 * Created by mo on 15/11/22.
 *
 * 通过反射写一个通用的设置某个对象的某个属性为指定值
 */
public class ReflectTest3 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        //测试
        Person obj = new Person("mo",22,"bj");
        System.out.println(obj);

        setProperty(obj,"name","xi");
        System.out.println(obj);
    }

    public static void setProperty(Object obj, String propertyName, Object value) throws NoSuchFieldException, IllegalAccessException {
        //获取字节码文件
        Class c = obj.getClass();
        //获取变量
        Field filed = c.getDeclaredField(propertyName);
        //取消访问检查
        filed.setAccessible(true);
        //设置值
        filed.set(obj,value);

    }
}

动态代理

动态代理:通过反射生成一个代理

Java中提供了java.lang.reflect.Proxy类和InvocationHandler接口来生成动态代理对象,但只能对接口做代理.日常开发中用更强大的代理框架如cglib等

代码演示:

package javareflect.Test4;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * Created by mo on 15/11/22.
 */
public class UserDaoDemo {
    public static void main(String[] args) {
        UserDao ud = new UserDaoImpl();
        ud.add();
        ud.del();
        ud.update();
        ud.find();
        System.out.println("------------------------------");
        //这里方法可以随便调用,事实上,在每个操作执行前应该查看该用户是否有权限进行此操作,并且在操作后留下记录
        //不修改以前写好的代码 防止出错,所以新创建个实现类,然后增加功能

        UserDao ud2 = new UserDaoImpl2();
        ud2.add();
        ud2.del();
        ud2.update();
        ud2.find();
        System.out.println("------------------------------");

        /*
        此时如果又有一个相似的类,也需要权限校验和日志记录功能,难道我又要重写一个实现类吗?
        明显,我们应该把权限校验和日志记录功能提取出来,单独做成一个中介,那么这里就要用到动态代理的知识点

        Proxy类中的这个方法可以创建动态代理对象:
        public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

        InvocationHandler里有一个方法需要实现:
Object invoke(Object proxy,Method method,Object[] args)
        要先实现InvocationHandler接口
         */
        //对ud对象做动态代理
        myInvocationHandler myHandler = new myInvocationHandler(ud);
        UserDao proxyUserDao = (UserDao) Proxy.newProxyInstance(ud.getClass().getClassLoader(), ud.getClass().getInterfaces(),myHandler);//生成代理并强转为UserDao类型
        proxyUserDao.add();
        proxyUserDao.del();
        proxyUserDao.find();
        proxyUserDao.update();
        System.out.println("------------------------------");
        //这样,如果别的类也需要这俩方法,只需在这里把ud对象换为该类对象即可

    }
}

/**
 * 这是做动态代理前要做的InvocationHandler接口实现类
 * 在接口的invoke方法里实现新增的功能,并通过传入该方法的Method对象调用目标对象的方法
 */
class myInvocationHandler implements InvocationHandler{

    private Object target;//对目标对象target做代理

    public myInvocationHandler(Object obj){
        target = obj;
    }
    /*
    proxy - 在其上调用方法的代理实例
    method - 对应于在代理实例上调用的接口方法的 Method 实例。
    args - 包含传入代理实例上方法调用的参数值的对象数组。
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("权限校验");
        //通过反射Method调用方法,传入调用方法的对象和参数
        Object result = method.invoke(target,args);//这是实际上就表示add del update find方法

        System.out.println("日志记录");

        return result;//返回的是代理对象,这个对象就具有了权限校验和日志记录功能
    }
}

/**
 * 这是通过创建新的类实现 权限校验和日志记录 功能
 */
class UserDaoImpl2 implements UserDao{

    @Override
    public void add() {
        System.out.println("权限校验");
        System.out.println("增加");
        System.out.println("日志记录");
    }

    @Override
    public void del() {
        System.out.println("权限校验");
        System.out.println("删除");
        System.out.println("日志记录");
    }

    @Override
    public void update() {
        System.out.println("权限校验");
        System.out.println("修改");
        System.out.println("日志记录");
    }

    @Override
    public void find() {
        System.out.println("权限校验");
        System.out.println("查询");
        System.out.println("日志记录");
    }
}
/**
 * Created by mo on 15/11/22.
 * 用户操作接口
 */
public interface UserDao {
    void add();
    void del();
    void update();
    void find();
}
/**
 * Created by mo on 15/11/22.
 * 用户操作实现类
 */
public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("增加");
    }

    @Override
    public void del() {
        System.out.println("删除");
    }

    @Override
    public void update() {
        System.out.println("修改");
    }

    @Override
    public void find() {
        System.out.println("查询");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值