反射机制(草稿)

反射机制作用:
通过java语言中的反射机制可以操作字节码文件(可以读和修改字节码文件)
通过反射机制可以操作代码片段。

反射机制的相关类在java.lang.reflect.*包下

反射机制相关的重要的类:

java.lang.Class 代表整个字节码,代表一个类型,代表整个类

java.lang.reflect.Method 代表字节码中的方法字节码,代表类中的方法

java.lang.reflect.Constructor 代表字节码中的构造方法字节码,代表类中的构造方法

java.lang.reflect.Field代表字节码中的属性字节码,代表类中的成员变量

public class User{
	//Field
	int no;
	//Constructor
	public User(int no){this.no=no}
	//Method
	public int getNo(){
	return no;
	}
}

在这里插入图片描述

/*
* 操作一个类的字节码,需要获得这个类的字节码。获取java.lang.Class实例
* 有三种方式
* 1.Class.forName()
*   1)静态方法
*   2)方法的参数是一个字符串
*   3)完整类名必须带有包名,java.lang不能省略
* */
public class Test01 {
    public static void main(String[] args) {
        Class c1=null;
        try {
             c1=Class.forName("java.lang.String");//c1代表string.class文件,c1代表string类型
            Class c2=Class.forName("java.lang.Date");//c2代表Date类型
            Class c3=Class.forName("java.lang.Integer");
            Class c4=Class.forName("java.lang.System");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    //方式2.java中任何一个对象都有一个方法getClass()

        String s="abc";
        Class x=s.getClass();//x代表string.class字节码文件,x代表string类型
        System.out.println(c1==x);//true(==判断的是对象的内存地址,这两个变量中保存的内存地址是一样的,都指向方法区中的字节码)

    //方式3.java语言中任何一种类型,包括基本数据类型,都有.class属性。
        Class y=String.class;//y代表string类型
        Class k= Date.class;//k代表Date类型
    }
}

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

        try {
            //通过反射机制获取Class,通过Class来实例化对象
            Class c=Class.forName("reflect.User");//c代表User类型
            //newInstance()这个方法会调用User这个类的无参构造方法,完成对象的创建。
            Object obj= c.newInstance();
            System.out.println(obj);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

/*验证反射机制的灵活性
*   java代码写一遍,在不改变java源代码的基础上,可以做到不同对象的实例化(properties文件里的对象类型随便改,反射代码不用变),非常灵活。
* 符合OCP开闭原则,对扩展开放,对修改关闭
* */
public class Test01 {
    public static void main(String[] args) throws Exception {
    //通过IO流读取classinfo.properties文件
        FileReader reader=new FileReader("JavaBase/classinfo.properties");
        //创建属性类对象Map
        Properties pro=new Properties();//key values都是string
        //加载
        pro.load(reader);
        //关闭流
        reader.close();

        //通过key获取value
        String className=pro.getProperty("className");
        //System.out.println(className);//结果:reflect.User

        //通过反射机制实例化对象
        Class c=Class.forName(className);
        Object obj=c.newInstance();
        System.out.println(obj);
    }
}

/*
* 研究Class.forName()发生了什么
* 如果只希望一个类的静态代码块执行,其他代码一律不执行。
* 可以使用Class.forName("完整类名");
* 这个方法的执行会导致类加载,静态代码块执行
* */
public class Test01 {
    public static void main(String[] args)  {
        try {
            //Class.forName()这个方法的执行会导致:类加载
            Class.forName("reflect.MyClass");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
class MyClass{
    //静态代码块在类加载时执行且只执行一次
    static{
        System.out.println("MyClass类的静态代码块执行了");
    }
}
/*
研究文件路径
该文件必须在类路径下(类路径:在src下的就是在类路径下)
src是类的根路径
* */
public class Test01 {
    public static void main(String[] args) throws Exception {
        /*
        * Thread.currentThread()当前线程对象
        *getContextClassLoader()是线程对象的方法,可以获取到当前线程的类加载器对象
        * getResource()[获取资源]这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源
        * */
    String path=Thread.currentThread().getContextClassLoader().getResource("classinfo.properties").getPath();
    //采用以上方式可以拿到一个文件的绝对路径
        ///C:/Users/user/IdeaProjects/test/out/production/JavaBase/classinfo.properties
    System.out.println(path);
    }
}

public class Test01 {
    public static void main(String[] args) throws Exception {
        //获取文件绝对路径
        //方式1
/*        String path = Thread.currentThread().getContextClassLoader()
                .getResource("classinfo.properties").getPath();
        FileReader reader=new FileReader(path);*/
        //方式2 直接以流的形式传送(不需要直接路径)
        InputStream reader=Thread.currentThread().getContextClassLoader().getResourceAsStream("classinfo.properties");
        Properties pro=new Properties();
        pro.load(reader);
        reader.close();
        //通过key 获取value
        String className=pro.getProperty("className");
        System.out.println(className);
    }
}
/*
* java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容
* 使用以下方式的时候,属性配置文件必须放在类路径下
* */
public class Test01 {
    public static void main(String[] args) {
    //资源绑定器,只能绑定xxx.properties文件。并且这个文件必须在类路径下,文件扩展名必须是properties
        //并且在写路径的时候,路径后面的扩展名不能写
        ResourceBundle bundle=ResourceBundle.getBundle("classinfo");

        String className=bundle.getString("className");
        System.out.println(className);
    }
}

关于JDK中自带的类加载器
什么是类加载器?
启动类加载器
扩展类加载器
应用类加载器

假设有这样一段代码:
string s=“abc”;

代码在开始执行之前,会将所需要类全部加载到JVM中。
通过类加载器加载,看到以上代码类加载器会找string.class文件,找到就加载,那么是怎么进行加载的呢?

首先通过“启动类加载器”加载。
注意:启动类加载器加载JDK最核心的类库

如果通过“启动类加载器”加载不到的时候
会通过“扩展类加载器”加载。

如果“扩展类加载器”没有加载到,那么会通过“应用类加载器”加载。
注意:应用类加载器专门加载:classpath中的jar包(class文件。)

java中为了保证类加载的安全,使用双亲委派机制。
优先从启动类加载器中加载,称为<父>
父中无法加载到,再从扩展类加载器中加载,
称为<母>。双亲委派,如果都加载不到,才会考虑从应用加载器中加载。

分割一下

/*反射Student类中所有Field*/
public class Test01 {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取整个类
        Class studentClass=Class.forName("reflect.Student");
        String className=studentClass.getName();
        System.out.println("完整类名"+className);

        String simpleName=studentClass.getSimpleName();
        System.out.println("简类名"+simpleName);
        //获取类中所有public修饰的Field
        Field[] fields=studentClass.getFields();
        System.out.println(fields.length);//
        //取出这个field
        Field f=fields[0];
        //取出这个field的名字
        String fieldName=f.getName();
        System.out.println(fieldName);

        //获取所有的Field
        Field[] fs=studentClass.getDeclaredFields();
        System.out.println(fs.length);//4
        //遍历
        for(Field field:fs){
            //获取属性修饰符列表
            int i=field.getModifiers();
            System.out.println(i);//返回的修饰符是一个数字,每个数字是修饰符的代号
            //可将代号转换成字符串
            String modifierString= Modifier.toString(i);
            System.out.println(modifierString);
            //获取属性的类型
            Class fieldType=field.getType();
           //完整类型名
            // String fName=fieldType.getName();
            //简单类型名
            String fName=fieldType.getSimpleName();
            System.out.println(fName);
            //获取属性的名字
            System.out.println(field.getName());
        }
    }
}
//反射属性Field
public class Student {
    //Field翻译为字段,就是属性/成员
    //4个Field分别采用了不同的访问控制权限修饰符
    public int no;//field对象
    private String name;
    protected int age;
    boolean sex;
}

反编译

/*通过反射机制,反编译一个类的属性Field*/
public class Test01 {
    public static void main(String[] args) throws ClassNotFoundException {
    //创建这个是为了拼接字符串
        StringBuilder s=new StringBuilder();

        Class studentClass=Class.forName("java.lang.Thread");

        s.append(Modifier.toString(studentClass.getModifiers())+" class "+studentClass.getSimpleName()+"{\n");
        Field[] fields=studentClass.getDeclaredFields();
        for(Field field:fields){
            s.append("\t");
            s.append(Modifier.toString(field.getModifiers()));
            s.append(" ");
            s.append(field.getType().getSimpleName());
            s.append(" ");
            s.append(field.getName());
            s.append(";\n");
        }
        s.append(")");
        System.out.println(s);
    }
}

/*
* 怎样通过反射机制访问一个java对象的属性?
* 给属性赋值set
* 获取属性的值get
* */
public class Test01 {
    public static void main(String[] args) throws Exception {
       //不使用反射机制访问对象的属性
        Student s=new Student();
       //给对象赋值
        s.no=111;
        //读属性值
        //两个要素
        System.out.println(s.no);

        //使用反射机制访问对象的属性(set get)
        Class studentClass=Class.forName("reflect.Student");
        Object obj=studentClass.newInstance();//obj就是student对象
        //获取no属性(根据属性的名称来获取field)
        Field noField=studentClass.getDeclaredField("no");

        //给obj对象(Student对象)的no属性赋值
        /*
        * 三要素:对象 属性 值
        * */
        noField.set(obj,22);
        //读取属性的值
        //两个要素:获取obj对象的no属性的值
        noField.get(obj);

        /*
        * 如果想要访问私有属性,可以使用打破封装的方式
        * */
        Field nameField=studentClass.getDeclaredField("name");
        //打破封装(这就是反射机制的缺点,会给不法分子留下机会)
        nameField.setAccessible(true);
        //给name属性赋值
        nameField.set(obj,"jackson");
        //获取name属性的值
        System.out.println(nameField.get(obj));
    }
}

/*
* 可变长度参数
* 语法是:类型...
1.可变长度参数要求参数个数:0~N个
2.可变长度参数在参数列表中必须在最后一个位置上,而且可变长度参数只能有一个
3.可变长度参数可以当做一个数组来看待
 */
public class Test01 {
    public static void main(String[] args) throws Exception {
       m();
       m(10);
       m3("abc","def","ghjm");
       String[] strs={"asfd","dsfcdv"};
       //也可以传一个数组
       m3(strs);
    }
    public static void m(int...args){
        System.out.println("执行m方法");
    }
    //必须在最后,只能有一个
    public static void m2(int a,String...args){}

    public static void m3(String... args){
        //args有length属性,说明args是一个数组
        //可将可变长度参数视为一个数组
        for (int i = 0; i < args.length; i++) {
            System.out.println(args[i]);
        }
    }
}

/*
* 反射Method
* */
public class Test01 {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取类
        Class userServiceClass=Class.forName("reflect.UserService");

        //获取所有的Method(包括私有)
        Method[] methods=userServiceClass.getDeclaredMethods();
        System.out.println(methods.length);//2

        //遍历Method
        for(Method method:methods){
            //获取修饰符列表
            System.out.println(Modifier.toString(method.getModifiers()));
            //获取方法的返回值类型
            System.out.println(method.getReturnType().getSimpleName());
            //获取方法名
            System.out.println(method.getName());
            //方法的修饰符列表(一个方法的参数可能会有多个
            Class[] parameterTypes=method.getParameterTypes();
            for(Class parameterType:parameterTypes){
                System.out.println(parameterType.getSimpleName());
            }
        }
    }
}
/*
* 用户业务类
* */
public class UserService {
    /***
     * 登录方法
     * @param name 用户名
     * @param password 密码
     * @return TRUE表示登陆成功,FALSE表示登陆失败
     */
    public boolean login(String name,String password){
        if("admin".equals(name)&&"123".equals(password)){
            return true;
        }
        return false;
    }
    /**
     * 退出登录的方法
     */
    public void logout(){
        System.out.println("系统已安全退出");
    }
}

/*
* 通过反射机制调用一个对象的方法
* 反射机制让代码更具通用性。
* */
public class Test01 {
    public static void main(String[] args) throws Exception {
        Class userServiceClass=Class.forName("reflect.UserService");
        //不用反射机制,调用方法
        //创建对象
        UserService userService=new UserService();
        //调用方法
        boolean loginSuccess=userService.login("admin","123");
        System.out.println(loginSuccess?"登陆成功":"登陆失败");

        /*******************************************************/
        //创建对象
        Object obj=userServiceClass.newInstance();
        //获取Method
        Method loginMethod=userServiceClass.getDeclaredMethod("login",String.class,String.class);
        //调用方法(要素:对象 方法名 实参列表 返回值)
        //invoke-----调用
        /**
         * loginMethod 方法
         * obj 对象
         * "admin","123"实参
         * retValue返回值
         */
        Object retValue=loginMethod.invoke(obj,"admin","123");
        System.out.println(retValue);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值