【Java学习记录】反射机制

       反射机制是Java程序开发语言的特征之一,它允许运行中的Java程序对自身进行检查,并可以直接操作程序的内部属性。参考动力节点杜老师的视频,总结了反射机制的基本知识点。

1、反射机制有什么用?

可以操作字节码文件即.class文件(读和修改)。

2、反射机制相关的类在那个包下?

java.lang.reflect.*

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

java.lang.Class                       代表了整个字节码文件  代表整个类

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

java.lang.reflect.Method        代表了字节码文件中的方法字节码

java.lang.reflect.Field             代表了字节码文件中的属性字节码

4、要操作字节码文件,首先要获取字节码文件,即获取java.lang.Class ,有三种方式,这三种方式均会导致类加载去加载这个要反射的类:

第一种方式:Class.forName();

public class ReflectTest{
    public static void main(String[] args){
        /*
           Class.forName()
            1、静态方法
            2、参数是字符串类型,需要的是完整的类名,即必须带包名
            3、这个方法会抛出ClassNotFoundException异常
             
        */
        Class c1 = Class.forName("java.lang.String");//c1就代表了String类
        Class c2 = Class.forName("java.util.Date");  //c2就代表了Date类
    }catch(ClassNotFoundException e){
        e.printStackTrace();
    }
}

第二种方式:Object类有一个getClass()方法,即每一个对象都有这个方法。

String s = "abc";
Class x = s.getClass();
System.out.println(x == c1);  
//输出true,即一个程序中只会加载一个String.class,因此两种方式得到的结果内存地址相同。

第三种方式:Java中任何一种类型,包括基本数据类型,都有.class属性

Class y1 = String.class;
Class y2 = Date.class;
Class y3 = int.Class;
System.out.println(y1 == c1);//对应的类内存地址同样相同

5、获取到Class后,我们能干什么呢?首先我们可以以此来实例化对象:

首先,这里有一个User类

package  com.exercise.test;

public class User{
    public User(){}
} 

要创建一个User对象,应该怎么做呢?

public class ReflectTest02{
    public static void main(String[] args){
        Class u1 = Class.forName("com.exercise.test.User");
        //newInstance()这个方法会调用User类的无参数构造方法,所以前提是User类有无参构造方法存在
        Object obj = u1.newInstance();
    }
}

这个时候你可能会问,费了这么大功夫居然只是为了创建对象?我直接new User()不行吗?这个时候便要说明一下反射机制到底灵活在哪里?

首先,我在模块src文件夹下创建一个属性配置文件classinfo.properties ,在文件中写上:

className=com.exercise.test.User

接下来,通过IO流读取这个文件,再交给反射机制:

public class ReflectTest02{
    public static void main(String[] args) throws Exception{
        /*
        //在src根目录下获取一个文件的绝对路径
        String path = Thread.currentThread().getContextClassLoader().
                               getResource("classinfo.properties").getPath();

        //IO流读取
        Reader reader = new FileReader(path);//括号里面可以写绝对路径
        */
        //或者直接以流的形式返回
        InputStream reader = Thread.currentThread().getContextClassLoader().
                               getResourceAsStraem("classinfo.properties");
        //创建属性类对象
        Properties pro = new Properties();
        //加载
        pro.load(reader);
        //关闭流
        reader.close
        //通过key获取value
        String className = pro.getProperties("className");
        //反射机制实例化对象
        Class u1 = Class.forName("className");
        //newInstance()这个方法会调用User类的无参数构造方法,所以前提是User类有无参构造方法存在
        Object obj = u1.newInstance();
    }
}

到了这一步相信你已经看出反射机制的灵活性了,是的,只要我修改配置文件中的类名(比如把com.exercise.test.User改为java.lang.String),就可以在不动代码的基础上,实现不同类的对象的创建

可能你会觉得这个这种方法好是好,但是代码太多了,而且记不住,没关系,还有更简便的,java.util包下提供了一个资源绑定器,便于获取属性配置文件的内容,当然也只限于在类路径下,也就是src路径下并且只能是属性配置文件,即xxx.properties

//注意路径的文件扩展名不能写!!
ResourceBundle bundle = ResourceBundle.getBundle("classinfo");
String className = bundle.getString("className");

6、操作字节码文件只能实例化对象吗?当然不是,接下来说明如何获取类中的属性:

这里有一个Student类,分别以四种修饰符修饰4个属性:

package  com.exercise.test;

public class Student{
    public int no;
    private String name;
    protected int age;
    boolean sex;
    public Student(){}
    public Student(int no){
        this.no = no;
    }
    public Student(int no,String name){
        this.no = no;
        this.name = name;
    }

} 

使用反射机制获取Student类的属性:

public class ReflectTest03 throws Exception{
    public static void main(String[] args){
        Class c1 = Class.forName("com.exercise.test.Student");
        String className = c1.getName();
        System.out.println("完整类名:"+className);   //获取完整类名 
                                                      //com.exercise.test.Student
        String simpleName = c1.getSimpleName();       
        System.out.println("简类名:"+className);       //获取简单类名 Student
        
        //获取类中所有public修饰的属性
        Field[] fields = c1.getFields();
        System.out.println(filelds.length);    //数组中只有一个元素
        //取出这个元素,得到它的名字
        System.out.println(filed[0].getName());    //得到 no

        //获取类中的所有属性
        Filed[] fs = c1.getDeclaredFields();
        System.out.println(fs.length);          //数组中有4个元素
        for(Field f : fs){
            //获取属性的修饰符列表
            int i = f.getModifiers();      //返回的是一个数字,是修饰符的代号
            //将这个数字转换成字符串
            String ms = Modifier.toString(i);
            System.out.println(ms);
            //获取属性的类型及类型名
            Class fieldType = f.getType();
            String typeName = fieldType.getName();
            System.out.println(typeName.getName);
            //获取属性名
            System.out.println(f.getName);       //输出no name age sex
        }
    }
}

那么如何访问对象属性呢?

public class ReflectTest04 throws Exception{
    public static void main(String[] args){
        Class c1 = Class.forName("com.exercise.test.Student")
        Object obj = c1.newInstance();
        //给no这个属性赋值,首先根据属性名拿到这个属性
        Field noField = c1.getDeclaredField("no");
        //调用set方法
        noField.set(obj,1111);    //给obj对象的no属性赋值1111
        //获取属性的值
        System.out.println(noField.get(obj));  //输出1111
    }
}

但是要注意,这种方法只能访问public修饰的属性,要想访问其他属性,需要先打破封装。

Field nameField = c1.getDeclaredField("name");
//打破封装
nameField.setAccessible(true);
nameField.set(obj,"jack");
System.out.println(nameField.get(obj));

如何调用方法呢?

这里有一个UserService类,里面有一些方法

public class UserService{

    public boolean login(String userName,String password){
        if("admin".equals(userName) && "123".equals(password)){
            return true;
        }
        return false;
    }
    public void logout(){
        System.out.println("系统已安全退出");
    }
}

现在我们来调用类中的方法:

public class ReflectTest05 throws Exception{
    public static void main(String[] args){
        Class c1 = Class.forName("com.exercise.test.UserService")
        //获取对象,没有对象不能调方法
        Object obj = c1.newInstance();
        //获取Method,Java中区别一个方法靠的是方法名和参数列表,所以
        Method loginMethod = c1.getDeclaredMethod(login,String.class,String.class);
        //调用方法,需要方法、对象、参数、返回值
        Object reValue = loginMethod.invoke(obj,"admin","123");    
     
    }
}

如何调用构造方法呢?

public class ReflectTest06 throws Exception{
    public static void main(String[] args){
        //获取User类
        Class c1 = Class.forName("com.exercise.test.User")
        //调用无参数构造方法
        Object o1 = c1.newInstance();

        //调用有参数构造方法
        //首先要获取到这个构造方法
        Constructor con = c1.getDeclaredConstructor(in.class,String.class);
        //调用构造方法new对象
        Object o2 = con.newInstance(111,"jack");
 
     
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值