反射机制详解(重点).

目录: 

一、反射机制概述
    获取Class的三种方式必须掌握

二、通过读属性文件实例化对象 [重点 框架的时候常用]

三、只让静态代码块执行可以使用Class.forName [重点 JDBC技术需要用到]

四、获取Field(属性)    【重点:通过反射机制完成下列三个要求】
    —— 获取Student1类中的属性/Field
    —— 获取Student1类中属性的类型
    —— 获取Student1类中属性的修饰符列表

五、反射Method(方法) 【重要】        和属性Field一样也有同样的三个要求
    —— 反射获取用户类的方法名/ 方法类型/ 方法修饰符列表/ 方法的参数类型名
    
——很重点:必须掌握,通过反射机制如何调用一个对象的方法

六、反射机制调用构造方法/Constructor

七、获取一个类的父类和父接口 【重点

一、反射机制概述

反射机制
1.1、反射机制有什么用?
通过java语言中的反射机制可以操作字节码文件。
优点类似于黑客。(可以读和修改字节码文件。)
通过反射机制可以操作代码片段。( class文件。)

1.2、反射机制的相关类在哪个包下?
    java.lang.reflect.*;

1.3、反射机制相关的重要的类有哪些?
    java.lang.class;    代表整个字节码,代表一个类型,代表整个类

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

    java.lang.reflect.Filed;    代表字节码中的属性字节码,代表类中的属性/成员变量(静态方法 实例方法)

    如下所示:
    java.lang.class:代表整个类
    public class User
    {    
        java.lang.reflect.Filed:代表类中的属性/成员变量(静态方法 实例方法)
        int no;
        
        java.lang.reflect.Constructor:代表类中的构造方法
        public User(){}
        public User(int no){this.no =no}
        
        java.lang.reflect.Method:代表类中的方法
        public void doSome(){}
    }

第一步: 

要操作一个类的字节码,需要首先获取到这个类的字节码,怎么获取java.lang.Class实例 

        有三种方式~

第一/二种获取Class的方式 

第一种方式:
    Class.forName()
        1、静态方法
        2、方法的参数是一个字符串
        3、字符串需要一个完整的类名(Class类下的静态方法forName)
        4、完整类名必须带有包名,java.lang包也不能省略(包名开始)

第二种方式:
        java中任何一个对象都有一个getClass()方法

代码演示如下: 

package bj.powernode.javase.reflect;
import java.util.Date;

public class reflectTest01 {
    public static void main(String[] args) {
        /*
            第一种方法:
            Class.forName()
                1、静态方法
                2、方法的参数是一个字符串
                3、字符串需要一个完整的类名(Class类下的静态方法forName)
                4、完整类名必须带有包名,java.lang包也不能省略
         */
        Class c1 =null;
        Class c2 =null;
        try {
            c1 =Class.forName("java.lang.String");  //  c1代表String.class文件,或者说c1代表String类型
            c2 =Class.forName("java.util.Date");  //  c2代表Date类型
            Class c3 =Class.forName("java.lang.Integer");   // c3代表Integer类型
            Class c4 =Class.forName("java.lang.System");    // c4代表System类型
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

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

        Date date =new Date();
        Class y =date.getClass();
        System.out.println(y==c2);  // true (可以得出结论:c2和y两个变量中保存的内存地址都是一样的)
        // 注意: 都是指向方法区的字节码文件
    }
}

 第三种获取Class的方式

// 第三种方式:
// Class c =任何类型.class;
package bj.powernode.javase.reflect;
import java.util.Date;

public class reflectTest01 {
    public static void main(String[] args) {
        // 第三种方式:
        // Class c =任何类型.class;

        // java语言中任何一种类型,包括基本数据类型,他都有.class属性
        Class x =int.class; // x代表int类型
        Class y =String.class;  // y代表String类型
        Class z =Date.class;    // z代表Date类型

    }
}

 第二步:

获取到Class,能干什么

代码演示如下: 

User类:

package bj.powernode.javase.reflect;

public class User {
    // 默认有一个无参构造方法
    // 所以以后尽量把无参构造写出来
}

测试程序:

 重点注意:Class.forName("bj.powernode.javase.reflect.User"); 从包名开始

2、 newInstance() 这个方法 会调用User这个类的无参构造方法,完成对象的创建(如果User类当中只有有参构造方法那么会报异常)

package bj.powernode.javase.reflect;

public class reflectTest01 {
    public static void main(String[] args) {
        // 通过反射机制,获取Class,通过Class来实例化对象 (获取User.class字节码文件) 获取User类
        try {
            Class c =Class.forName("bj.powernode.javase.reflect.User"); // c代表User类型
            
            //  newInstance() 这个方法会调用User这个类的无参构造方法,完成对象的创建(如果User类当中只有有参构造方法那么会报异常)
            Object o =c.newInstance();  //  相当于 User user =new User();    // 多态
            System.out.println(o);  // bj.powernode.javase.reflect.User@1b6d3586

        }
        catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

二、通过读属性文件实例化对象 [重点 框架的时候常用]

验证反射机制的灵活性
    java代码写一遍,在不改变java源代码的基础上,可以做到不同对象的实例化 非常灵活~

代码演示如下: 

配置文件:

 User类:

 程序测试(可利用资源绑定器):

注意:以下代码是灵活的,代码不需要改动,可以修改配置文件(UserFile.properties),配置文件修改之后,可以创建出不同的实例对象

package bj.powernode.javase.reflect;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
/*
验证反射机制的灵活性
    java代码写一遍,在不改变java源代码的基础上,可以做到不同对象的实例化 非常灵活~
 */

public class reflectTest02 {
    public static void main(String[] args) {
        // 这种方式代码就写死了,只能创建一个User类型的对象
        // User user =new User();

        // 以下代码是灵活的,代码不需要改动,可以修改配置文件(UserFile.properties),配置文件修改之后,可以创建出不同的实例对象
        try {
            // 通过IO流读取UserFile.properties文件
            FileReader fr =new FileReader("chapter9\\src\\bj\\powernode\\javase\\reflect\\UserFile.properties");
            // 创建Map集合
            Properties pro =new Properties();   // key和value都是String类型
            // 把配置文件加载到Map集合
            pro.load(fr);
            // 关流
            fr.close();

            // 通过key获取value
            String s =pro.getProperty("username");
            // String s =pro.getProperty("username1");
            //  这里可以通过对配置文件key获取不同的实例对象
            // System.out.println(s);  // bj.powernode.javase.reflect.User

            // 通过反射机制 拿到User的Class文件
            Class c =Class.forName(s);

            // 相当于实例化对象 User user =new User();
            Object o =c.newInstance();  // 这个newInstance方法调用c(User)类的无参构造方法
            System.out.println(o);



        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }
}

 

三、只让静态代码块执行可以使用Class.forName [重点 JDBC技术需要用到]

Class.forName()方法 会导致类加载 (类名.class文件) 

代码演示如下: 

package bj.powernode.javase.reflect;
/*
    Class.forName()方法 会导致类加载 (类名.class文件)
 */
public class reflectTest03 {
    public static void main(String[] args) {

        // Class.forName()方法的执行会导致:类加载
        try {
            Class.forName("bj.powernode.javase.reflect.Student");   // 这里相当于类加载: Student.class 在方法区
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

class Student{

    // 静态代码块在类加载时执行(方法区),并且只执行一次
    // 因为Class.forName()方法把Student类加载了,所以在方法区执行了这个静态代码块
    static {
        System.out.println("Student类的静态代码块开始执行了~");
    }

}

输出结果:

四、获取Field(属性)

1、获取Student1类中的属性/Field:

测试结论:

1、getFields()这个方法 是获取类中所有的public修饰的属性
2、getDeclaredFields()方法 获取类中的所有属性 

Student1类:

package bj.powernode.javase.reflect;
/*
反射属性Field
 */
public class Student1 {

    // Filed 翻译为字段,其实就是属性/成员
    // 4个Field  分别采用了不同的访问控制权限修饰符
    public int no;
    private String name;
    protected int age;
    boolean sex;

}

注意下列代码中出现的取名字的方法(其实都一个性质):

如:只不过简化名字而已

  拿到返回值类型的名字
            System.out.println(MethodType.getName());
            // System.out.println(MethodType.getSimpleName());  // 简化名字(修饰符列表/类型都可以用) 和上面其实一样

程序测试:

package bj.powernode.javase.reflect;
import java.lang.reflect.Field;

/*
反射Student1类当中的所有的Field(属性)

 */
public class reflectTest07 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {

        //  获取整个类   Student1.Class
        Class Student1Class =Class.forName("bj.powernode.javase.reflect.Student1");

        // 获取类中的所有public修饰的属性Field
        Field[] fields =Student1Class.getFields();  // 以数组形式返回
        System.out.println(fields.length);  // 1
        // 思考 Student1类当中有4个属性 为什么获取所有属性后,数组里面就一个元素呢?
        /*
            得出结论: getFields()这个方法 是获取类中所有的public修饰的属性/Field
         */
        // 取出这个元素 看看元素是谁
        Field f =fields[0];
        // 拿这个元素名字
        System.out.println(f.getName());    // no

        System.out.println("=========怎么获得Student1类中的所有的属性呢?============");

        // 获取Student1类中的所有属性    getDeclaredFields()方法
        // 遍历
        Field[] fields1 =Student1Class.getDeclaredFields();
        for (Field data:fields1){
            System.out.println(data.getName()); // 属性全部拿到
        }
    }
}

2、获取Student1类中属性的类型:

代码如下所示:

package bj.powernode.javase.reflect;
import java.lang.reflect.Field;

/*
获取Student1类中属性的类型
 */
public class reflectTest08 {
    public static void main(String[] args) throws ClassNotFoundException {
        // 获取整个类 Student1.class
        Class c =Class.forName("bj.powernode.javase.reflect.Student1");

        // 获取Student1类中属性
        Field[] fields =c.getDeclaredFields();

        // 获取属性中的类型
        for (Field data:fields){
            // 获取属性类型
            Class fieldType =data.getType();
            // 获取属性类型的名字
            String fieldName =fieldType.getName();
            System.out.println(fieldName);

        }
    }
}

 3、获取Student1类中属性的修饰符列表:

package bj.powernode.javase.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/*
获取Student1类中属性的修饰符列表
 */
public class reflectTest09 {
    public static void main(String[] args) throws ClassNotFoundException {

        // 获取整个类 Student1.class
        Class c =Class.forName("bj.powernode.javase.reflect.Student1");

        // 获取类中所有的属性/Field
        Field[] fields =c.getDeclaredFields();
        for (Field data:fields){
            // 获取所有属性名
            // System.out.println(data.getName());

            // 获取所有属性的修饰符列表     getModifiers()方法
            int i =data.getModifiers();
            System.out.println(i);   // 返回的修饰符是一个数字,每个数字是修饰符的代号~
            // 可以将这个修饰符"代号"数字转换成"字符串"吗?
            String s =Modifier.toString(i);     // Modifier类当中的一个toString()方法 
            System.out.println(s);
        }
    }
}

重点:通过反射机制访问对象属性 【重点框架会用,记得常回顾理解】

Student1类:

package bj.powernode.javase.reflect;
/*
反射属性Field
 */
public class Student1 {

    // Filed 翻译为字段,其实就是属性/成员
    // 4个Field  分别采用了不同的访问控制权限修饰符
    public int no;
    private String name;
    protected int age;
    boolean sex;

}

反射机制让代码变复杂了,但是灵活~

package bj.powernode.javase.reflect;
/*
通过反射机制访问对象属性
    不使用反射机制和使用反射机制都遵守三要素:
        要素1:obj对象
        要素2:no属性
        要素3:2222的值
 */

import java.lang.reflect.Field;

public class reflectTest10 {
    public static void main(String[] args) throws Exception {
        // 我们不使用反射机制,怎么去访问一个对象的属性呢?
        Student1 stu =new Student1();

        // 给属性赋值
        stu.no =1111;    // 对象调用

        // 读属性值
        System.out.println(stu.no);

        // 使用反射机制怎么去访问一个对象的属性呢?
        // 获取整个类 Student1.class
        Class c =Class.forName("bj.powernode.javase.reflect.Student1");

        Object o =c.newInstance();  // 相当于拿到 Student1 stu =new Student1();对象
        // 注意:调用newInstance()方法 调用的是Student1类的无参数构造方法,如果只有有参数构造的话会报异常

        // 获取no属性(根据属性的名称来获取属性)
        Field field =c.getDeclaredField("no");

        // 给o对象(Student1对象)的no属性赋值
        field.set(o,2222);  // 给o对象属性赋值2222

        // 读取属性的值
        System.out.println(field.get(o));
    }
}

 可以访问私有的属性吗 

代码演示如下:

package bj.powernode.javase.reflect;
import java.lang.reflect.Field;

/*
    访问私有的属性需要打破封装
 */
public class reflectTest1 {
    public static void main(String[] args) throws Exception{

        // 获取整个类
        Class c =Class.forName("bj.powernode.javase.reflect.Student1");

        Object o =c.newInstance();  // 相当于拿到 Student1 stu =new Student1();对象
        // 注意:newInstance()方法底层调用的是Student1类的无参构造方法

        // 获取o对象(Student1对象) name属性
        Field field =c.getDeclaredField("name"); // 私有属性

        // 给o对象name属性赋值
        field.set(o,"kitty");

        // 获取o对象的name属性
        System.out.println(field.get(o));
    }
}

输出结果:会出现异常

 可以通过打破封装进行访问对象的私有属性,因此这种方式是不安全的

 

五、反射Method/方法 [重要]        和属性Field一样

用户类:

package bj.powernode.javase.reflect;
/*
 用户业务类
 */
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("系统以安全退出");
    }
}

1、反射获取用户类的方法名/ 方法类型/ 方法修饰符列表/ 方法的参数类型名

代码演示如下:

package bj.powernode.javase.reflect;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/*
反射Method(方法)
 */
public class MethodTest01 {
    public static void main(String[] args) throws Exception{

        // 拿到整个类 UserService.class
        Class c =Class.forName("bj.powernode.javase.reflect.UserService");

        // 获取所有的Method/方法(包括私有方法)
        Method[] method =c.getDeclaredMethods();
        System.out.println(method.length);  // 2    UserService类当中有两个方法

        // 遍历Method
        for (Method data:method){
            System.out.println(data.getName()); // 拿到方法名

            // 获取方法的返回值类型
            Class MethodType =data.getReturnType();
            // 拿到返回值类型的名字
            System.out.println(MethodType.getName());
            // System.out.println(MethodType.getSimpleName());  // 简化名字(修饰符列表/类型都可以用) 和上面其实一样

            // 获取方法的修饰符列表
            int MethodF =data.getModifiers();
            // System.out.println(MethodF);    // 返回的修饰符是一个数字,每个数字是修饰符的代号~
            // 可以将这种代号转换成字符串格式
            String s =Modifier.toString(MethodF);
            System.out.println(s);  // 获取了修饰符列表的名字

            // 获取方法的参数类型(参数最重要的看是什么类型)
            Class[] parameterTypes =data.getParameterTypes();
            for (Class data1:parameterTypes){
                // 拿到参数类型名
                System.out.println(data1.getSimpleName());  // 获取了参数类型名字
            }

        }
    }
}

 很重点:必须掌握,通过反射机制如何调用一个对象的方法代码如下所示:

UserService类:

package bj.powernode.javase.reflect;
/*
 用户业务类
 */
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;
    }


    /**
     * 这里要特别的注意~
     * 有可能这个UserService类当中可能还有一个同名的login()方法 【重载机制】
     * 所以java中怎么区分一个方法: 依靠方法名和参数列表  【重点】
     * @param i i 参数
     */
    public void login(int i){
        System.out.println("重载机制~");
    }

    public void logout(){
        System.out.println("系统以安全退出");
    }
}

程序测试代码如下:

   反射机制:让代码很具有通用性,可变化的内容都是写到配置文件当中,将来修改配置文件之后,
    创的对象不一样了,调用的方法也不同了,但是java代码不需要做任何改动,这就是反射机制的
    魅力所在    
    直接修改Class c =Class.forName("bj.powernode.javase.reflect.UserService");
    把括号里面整成配置文件路径直接通过修改配置文件来调用不同类的方法

 

package bj.powernode.javase.reflect;
import java.lang.reflect.Method;

/*
重点:必须掌握,通过反射机制如何调用一个对象的方法呢

 */
public class MethodTest02 {
    public static void main(String[] args) throws Exception{

        // 不使用反射机制调用对象方法

        UserService user =new UserService();
        boolean b =user.login("admin","123");
        System.out.println(b ? "登录成功":"登录失败");  // 三元运算符 登录成功



        // 使用反射机制如何来调用一个对象的方法

        // 获取整个类 UserService.class
        Class c =Class.forName("bj.powernode.javase.reflect.UserService");

        // 获取创建对象
        Object o =c.newInstance();  // 相当于获得 UserService user =new UserService();对象,底层调用的是UserService类的无参数构造方法

        // 获取login登录方法 Method   因为UserService类当中方法可能发生重载,所以需要获得方法名和参数列表
        Method loginMethod =c.getDeclaredMethod("login",String.class,String.class); // 两个String类型  这里背也要背住~~~~~反射机制中最重要的一个方法~

        // 调用方法 [相当于o对象invoke调用loginMethod登录方法 然后传两个参数]
        Object o1 =loginMethod.invoke(o,"admin","123");    // 这里就相当于new对象 调用方法传参
        System.out.println(o1);    // true





    }
}

 六、反射机制调用构造方法/Constructor

客户类:

package bj.powernode.javase.reflect;
/*
    客户类
 */
public class Customer {
    int no;
    String name;
    String birth;
    boolean sex;

    // 无参数构造方法
    public Customer(){}

    // 有参数构造方法
    public Customer(int no,String name,String birth,boolean sex){
        this.no =no;
        this.name =name;
        this.birth =birth;
        this.sex =sex;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", birth='" + birth + '\'' +
                ", sex=" + sex +
                '}';
    }
}

程序测试:

package bj.powernode.javase.reflect;
import java.lang.reflect.Constructor;

/*
    通过反射机制调用类的构造方法
 */
public class ConstructorTest01 {
    public static void main(String[] args) throws Exception{

        // 不使用反射机制的时候如何调用构造方法

        // 调用无参数构造方法
        // Customer customer =new Customer();

        // 调用有参数构造方法
        Customer customer =new Customer(9,"kitty","2008-6-9",true);
        System.out.println(customer.name);  // kitty


        // 使用反射机制调用构造方法

        // 获取类 Customer.class
        Class c =Class.forName("bj.powernode.javase.reflect.Customer");

        // 创建对象     相当于 Customer c =new Customer();
        // 反射调用无参数构造方法
        Object o =c.newInstance();   // bewInstance()方法底层调用的是Customer类的无参数构造方法

        // 如何调用有参数构造方法

        // 第一步:先获取这个有参数构造方法 (和方法一样)
        Constructor con =c.getDeclaredConstructor(int.class,String.class,String.class,boolean.class);
        // 第二步:调用构造方法new对象(调用有参构造方法)  这里背也要背住
        Object o1 =con.newInstance(9,"kitty","2008-6-9",true);  // 这里就相当于new对象 然后传参数
        System.out.println(o1); // 相当于输出的c Customer c =new Customer(); 输出对象引用 自动调用toString方法

        // 调用无参数构造方法

        // 第一步:先获取这个无参数构造方法
        Constructor con1 =c.getDeclaredConstructor();
        // 第二步:调用构造方法new对象
        Object z =con1.newInstance();
        System.out.println(z);  // 无参数构造方法默认给属性赋值
    }
}

 七、获取一个类的父类和父接口 【重点】

 代码演示如下:

package bj.powernode.javase.reflect;
/*
    获取一个类的父类和实现的接口
 */
public class reflectTest {
    public static void main(String[] args) throws Exception{

        // 以String举例
        Class c =Class.forName("java.lang.String");

        // 获取String的父类  [默认继承object类]
        Class c1 =c.getSuperclass();
        // 拿到父类的名字
        System.out.println(c1.getName());   // java.lang.Object
        System.out.println(c1.getSimpleName()); // Object

        // 获取String的父接口
        Class[] classes =c.getInterfaces();
        // 遍历父接口拿父接口名字
        for (Class data:classes){
            System.out.println(data.getName()); // java.io.Serializable
            // System.out.println(data.getSimpleName());   // Serializable
        }
    }
}

 

  • 9
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值