学习笔记:反射机制

学习内容:反射

1. 反射的概述

1.1、什么是反射?
反射机制是JAVA语言提供的一种基础功能,赋予程序在运行时自省的能力,通过反射,我们可以直接操作类或者对象,必须获取类的定义,获取类声明的属性与方法,调用方法或者构造对象,甚至可以在运行时修改类的定义。

1.2、反射机制的作用?
可以通过java语言中的反射机制可以操作字节码文件。
(反射机制相关的重要的类都在java.lang.reflect.*下)
1.3、反射机制相关的重要的类有哪些?
java.lang.Class 代表整个字节码,代表一个类型,代表整个类
java.lang.reflect.Method 代表字节码中的方法节码,代表类中的方法
java.lang.reflect.Constructor 代表字节码中的构造方法字节码,代表类中的构造方法
java.lang.reflect.Field 代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)

2. 获取Class的三种方式

2.1、第一种方式:Class c=Class.forName(“完整类名带包名”);
java.lang.Class类中的方法Class.forName()
(1)静态方法
(2)方法的参数是一个字符串
(3)字符串需要的是一个完整的类名
(4)完整的类名必须带有包名。java.lang也不能省略
(5)会导致类加载

public class Test01 {
    public static void main(String[] args) {
        try {
            Class c1=Class.forName("java.lang.String");//c1代表String.class,或者说c1代表String类型
            Class c2=Class.forName("java.util.Date");//c2代表Date类型
            Class c3=Class.forName("java.lang.Integer");//c3代表Integer类型
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

验证Class.forName()方法导致类加载:

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

            Class.forName("Fs.MyClass");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

class MyClass{
    //静态代码块在类加载时执行,并且只执行一次
    static {
        System.out.println("MyClass类的静态代码块执行了");
    }
}

在这里插入图片描述
获取类名:(java.lang.Class中的方法)
String getName() 获取类的完整类名
String getSimpleName() 获取类的简类名

//获取整个类
        Class studentClass=Class.forName("Fs.Student");

        //获取类的完整类名
        String className=studentClass.getName();
        System.out.println(className);//Fs.Student

        //获取类的简类名
        String simpleName=studentClass.getSimpleName();
        System.out.println(simpleName);//Student

2.2、第二种方式:Class c=对象.class();
java中任何一个对象都有一个方法:getClass()

public class Test01 {
    public static void main(String[] args) {
        Class c1=null;
        Class c2=null;
        try {
             c1=Class.forName("java.lang.String");
             c2=Class.forName("java.util.Date");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        //java中任何一个对象都有一个方法:getClass()
        String s="abc";
        Class x=s.getClass();//x代表String.class字节码文件,x代表String类型
        System.out.println(c1==x);//true(==判断的是对象的内存地址)

        Date time=new Date();
        Class y=time.getClass();
        System.out.println(c2==y);//true (c2和y两个变量中保存的内存地址都是一样的,都指向方法区中的字节码文件)
    }
}

在这里插入图片描述
2.3、第三种方式:Class c=任何类型.class;
java语言中任何一种类型,包括基本数据类型,它都有.class属性。

public class Test01 {
    public static void main(String[] args) {
        Class z=String.class;//z代表String类型
        Class k= Date.class;//k代表Date类型
        Class f=int.class;//f代表int类型
    }
}

2.4、获取到Class,能干什么?
(通过反射实例化对象)

public class User {
    public User() {
        System.out.println("无参数构造方法!");
    }
}

public class Test01 {
    public static void main(String[] args) {
        try {
            //通过反射机制,获取Class,通过Class来实例化对象
            Class c=Class.forName("Fs.User");
            //newInstance()这个方法会调用User这个类的无参数构造方法,完成对象的创建
            Object obj=c.newInstance();
            System.out.println(obj);//Fs.User@4eec7777
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

2.5、研究一下文件路径的问题

FileReader reader=new FileReader("Fs\\user.properties");
这种方式的路径缺点:移植性差,在IDEA种默认的当前路径是project的根。
这个代码假设离开了IDEA,换到其他位置,可能当前路径就不是project的根了,这个路径就失效了。

接下来说一种比较通用的一种路径。即使代码换位置了,这种编写仍然是通用的。
注意:使用一下通用方式的前提的:这个文件必须在类路径下。
public class Test01 {
    public static void main(String[] args) {
        //Thread.currentThread() 当前线程
        //getContextClassLoader() 是线程对象的方法,可以获取到当前线程的类加载器对象
        //getResource() (获取资源)这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源
        String path=Thread.currentThread().getContextClassLoader().getResource("user.properties").getPath();
        采用以上代码可以拿到一个文件的绝对路径
        System.out.println(path);///D:/IDEA/Project/Serein/out/production/Serein/user.properties
    }
}

直接以流的形式返回:

public class Test01 {
    public static void main(String[] args) throws Exception {
        //获取一个文件的绝对路径
        /*
        String path=Thread.currentThread().getContextClassLoader().getResource("user.properties").getPath();
        FileReader reader=new FileReader(path);
         */
        //直接以流的形式返回
        InputStream reader=Thread.currentThread().getContextClassLoader().getResourceAsStream("user.properties");

        Properties pro=new Properties();
        pro.load(reader);
        reader.close();
        //通过key获取value
        String className=pro.getProperty("");
        System.out.println(className);
    }
}

3. 资源绑定器

java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容。(属性配置文件(xxx.properties)必须放在类路径下)
在这里插入图片描述

public class Test01 {
    public static void main(String[] args) throws Exception {
        //资源绑定器,只能绑定xxx.properties文件,并且这个文件必须在类路径下。
        //文件扩展名也必须是properties,并且在写路径的时候后面的扩展名不能写
        ResourceBundle bundle =ResourceBundle.getBundle("user");
        String className=bundle.getString("className");
        System.out.println(className);//java.util.Date
    }
}

4. 反射Field(属性)

4.1、获取类中的属性:(java.lang.Class中的方法)
Field[] getFields() 获取类中所有public修饰的的属性
Field[] getDeclaredFields() 获取所有的属性
Field getDeclaredField(String name) 根据属性名称来获取属性

public class Student {
    //Field翻译为字段,其实就是属性/成员
    //4个Field,分别采用了不同的访问控制权限修饰符
    public int no;
    private String name;
    protected  int age;
    boolean sex;
    public static final double PI=3.1415926;
}
//获取整个类
Class studentClass=Class.forName("Fs.Student");
//获取类中所有public修饰的的Field:Field[] getFields()
Field[] fields =studentClass.getFields();
System.out.println(fields.length);//测试数组中元素个数:2

//获取所有的Field
Field[] fs=studentClass.getDeclaredFields();
System.out.println(fs.length);//测试数组中元素个数:5

4.2、获取属性的修饰符列表:
int getModifiers() 获取修饰符,以整数编码
static String toString(int mod) 将这个修饰符的整数编码转换成字符串()(java.lang.reflect.Modifier中的静态方法)

//获取属性的修饰符列表
int i=field.getModifiers();//返回的修饰符是一个数字,每个数字是修饰符的代号
//System.out.println(i);
//可以将这个“代号”数字转换成“字符串”吗?
String modifierString= Modifier.toString(i);
System.out.println(modifierString);

在这里插入图片描述

4.3、获取属性的类型:
类<?> getType() 返回属性的类型(java.lang.reflect.Field中的方法)

//获取属性的类型
Class fieldType=field.getType();
String fName=fieldType.getName();
System.out.println(fName);

在这里插入图片描述

4.4、获取属性的名字:
String getName() 获取属性的名字

//获取属性的名字
System.out.println(field.getName());

在这里插入图片描述
4.5、通过反射机制访问对象属性

public class Student {
    //Field翻译为字段,其实就是属性/成员
    //4个Field,分别采用了不同的访问控制权限修饰符
    public int no;
    private String name;
    protected  int age;
    boolean sex;
    public static final double PI=3.1415926;
}
public class Test01 {
    public static void main(String[] args) throws Exception {
       //获取整个类
        Class studentClass=Class.forName("Fs.Student");//这里的异常懒得弄,就直接上抛了
        Object obj=studentClass.newInstance();//obj就是Student对象(底层调用无参数构造方法实例化对象)

        //获取no属性(根据属性名称来获取属性)
        Field noField=studentClass.getDeclaredField("no");

        //给obj对象(Student对象)的no属性赋值
        /*
        普通赋值方法:s.no=1111;给s对象的no属性赋值1111
        要素1:对象s ;要素2:no属性  ;要素3:1111值。

        虽然使用了反射机制,但是赋值的三要素还是缺一不可:
        要素1:obj对象
        要素2:no属性
        要素3:2222值
         */
        noField.set(obj,2222);//给obj对象的no属性赋值2222

        //读取属性的值
        /*
        System.out.println(s.no);//获取s对象的no属性
        两要素:1、s对象 2、no属性

        下面的也是一样,两个要素:1、s对象 2、no属性
         */
        System.out.println(noField.get(obj));//2222
        
        //可以访问私有的属性
        Field nameField=studentClass.getDeclaredField("name");
        //打破封装
        //这样设置完后,在外部也是可以访问private的
        nameField.setAccessible(true);
        //给name属性赋值
        nameField.set(obj,"Serein");
        //获取name属性的值
        System.out.println(nameField.get(obj));//Serein
    }
}

5. 可变长度参数

语法:

类型...(注意:一定是三个点)

1、可变长度参数要求参数个数是:0~N个
2、可变长度参数在参数列表中必须在最后一个位置上,而且可变长度参数只能有1个。
3、可变长度参数可以当做一个数组来看待

public class Test02 {
    public static void main(String[] args) {
        m();
        m(10);
        m(10,20);
        m3("a","b","c");

        //也可以传一个数组
        //String[] strs={"x","y","z"};
        //m3(strs);
    }

    public static void m(int...args){
        System.out.println("m方法执行了");
    }

    public static void m3(String...args){
        for (int i=0;i< args.length;i++){
            System.out.println(args[i]);
        }
    }
}

在这里插入图片描述

6. 反射Method(方法)

public class UserService {
    //登录方法
    public boolean login(String name,String password){
        if ("admin".equals(name)&&"123".equals(password)){
            return true;
        }
        return false;
    }

    //退出系统方法
    public void  logout(){
        System.out.println("系统已经安全退出!");
    }
}

6.1、获取所有的方法:方法[] getDeclaredMethods()java.lang.Class中的方法)
方法 getDeclaredMethod(String name, 类<?>... parameterTypes) 根据方法名和方法参数获取方法

Method[] methods=userService.getDeclaredMethods();

6.2、获取修饰符列表(和上面的一样)

System.out.println(Modifier.toString(method.getModifiers()));

6.3、获取返回值类型:类<?> getReturnType()java.lang.reflect.Method中的方法)

System.out.println(method.getReturnType().getSimpleName());

6.4、获取方法名:getName()

System.out.println(method.getName());

6.5、获取方法参数的修饰符列表(一个方法的参数可能会有多个):类<?>[] getParameterTypes()java.lang.reflect.Method中的方法)

Class[] parameterTypes= method.getParameterTypes();
for (Class parameterType:parameterTypes) {
       ystem.out.println(parameterType.getSimpleName());
}

6.6、反射机制调用方法:Object invoke(Object obj, Object... args)

public class UserService {
    //登录方法
    public boolean login(String name,String password){
        if ("admin".equals(name)&&"123".equals(password)){
            return true;
        }
        return false;
    }

    //可能还有一个同名login方法
    public void login(int i){

    }

    //退出系统方法
    public void  logout(){
        System.out.println("系统已经安全退出!");
    }
}
public class Test03 {
    public static void main(String[] args) throws Exception {
        //获取类
        Class userService=Class.forName("Fs.UserService");

        //获取所有的Method
        Method[] methods=userService.getDeclaredMethods();

        //创建对象
        Object obj=userService.newInstance();

        //获取方法
        //获取到三个参数的login方法
        Method loginMethod1 =userService.getDeclaredMethod("login", String.class, String.class);
        //获取到一个参数的login方法
        Method loginMethod2 =userService.getDeclaredMethod("login",int.class);

        //调用方法:Object invoke(Object obj, Object... args) 
        /*
        boolean loginSuccess=userService.login("admin","123");
        调用方法要素分析:
        要素1:对象userService
        要素2:login方法名
        要素3:实参列表
        要素4:返回值
         */
        Object retValue=loginMethod1.invoke(obj,"admin","123");
        System.out.println(retValue);//true
    }
}

7. 反射Constructor(构造方法)

(和反射方法差不多)

public class Vip {
    int no;
    String name;
    String birth;
    boolean sex;

    public Vip() {
    }

    public Vip(int no) {
        this.no = no;
    }
}
public class Test04 {
    public static void main(String[] args) throws Exception{
        //获取类
        Class vipClass=Class.forName("Fs.Vip");

        //获取所有的构造方法:Constructor<?>[] getDeclaredConstructors() 
        // 获取单个的构造方法:Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
        Constructor[] constructors=vipClass.getDeclaredConstructors();

        //遍历
        for (Constructor constructor:constructors){
            //获取构造方法的修饰符列表
            System.out.println(Modifier.toString(constructor.getModifiers()));
            //获取构造方法的方法名
            System.out.println(vipClass.getSimpleName());
            //获取构造方法参数列表的返回值类型
            Class[] parameterTypes =constructor.getParameterTypes();
            for(Class parameterType: parameterTypes){
                System.out.println(parameterType.getSimpleName());
            }
        }
    }
}

反射机制调用构造方法:

public class Vip {
    int no;
    String name;
    String birth;
    boolean sex;

    public Vip() {
    }

    public Vip(int no, String name, String birth, boolean sex) {
        this.no = no;
        this.name = name;
        this.birth = birth;
        this.sex = sex;
    }
}
public class Test04 {
    public static void main(String[] args) throws Exception{
        //获取类
        Class vipClass=Class.forName("Fs.Vip");

        //调用无参数构造方法
        Object obj=vipClass.newInstance();

        //调用有参数的构造方法
        //第一步:先获取到这个有参数的构造方法
        Constructor con=vipClass.getDeclaredConstructor(int.class,String.class,String.class,boolean.class);
        //第二:调用构造方法new对象
        Object newObj=con.newInstance(100,"Sersin","2001-01-01",true);

        //获取无参构造new对象
        Constructor con2=vipClass.getDeclaredConstructor();
        Object newObj2=con2.newInstance();
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值