Java基础语法之反射_字节码文件_创建对象_成员变量_成员方法_应用

反射

什么是反射?
      能够获取当前某个类的字节码文件对象Class,那么就可以获取当前类的构造器并且创建当前类实例,
      还可以获取当前类的成员变量并去赋值,或者获取当前类的成员方法并去调用!
1.字节码文件
如何获取一个类的字节码文件对象?
      1)Object类的getClass()获取  获取当前某个类的实例(正在运行的类)
      2)任意Java类型的class属性
      3)Class类的静态功能
      	public static Class<?> forName(String className)

Person类—下同

public class Person {
    //成员变量
     private String name ;//姓名 私有
     public int age ; //年龄  默认
     String address ; //地址 默认修饰符
    //无参构造方法:公共的
    public Person(){}
    //提供带两个参数的构造方法,默认修饰符
    Person(String name,int age){
        this.name = name ;
        this.age = age ;
    }
    //提供三个参数的构造方法,私有的
    private Person(String name,int age,String addrss){
        this.name = name ;
        this.age = age ;
        this.address = addrss ;
    }
    //提供一些成员方法(非静态)
    public void show(){
        System.out.println("show Person");
    }
    private  String functioin(String str){
        return str ;
    }
    void method(String message,int num){
        System.out.println(message+num);
    }
    public int method2(){
        return 100 ;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                '}';
    }
}
public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取Person类的字节码文件对象:Class
        Person p = new Person() ;
        Class c1 = p.getClass();
        System.out.println(c1);//class 全限定名称
        Person p2  = new Person() ;
        Class c2 = p2.getClass() ;
        System.out.println(c2);
        System.out.println(c1==c2);
        System.out.println(p==p2);//两个对象
        System.out.println("----------------------------");
        Class c3 = Person.class ;
        System.out.println(c3);
        System.out.println(c1==c3);
        System.out.println("-----------------------------");
        //知道某个一类的全限定名称了,也可以获取当前类的字节码文件对象
       // Class c4 = Class.forName("Person"); com.qf.reflect_03.Person //参数字符串:全限定名称
        Class c4 = Class.forName("com.qf.reflect_03.Person"); //com.qf.reflect_03.Person //参数字符串:全限定名称
        System.out.println(c4);
        System.out.println(c1==c4);
        //第二种方式和第三种使用  推荐第三种       因为参数为String------>就可以存储在配置文件中
    }
}
2.创建对象
1.获取构造方法:
	1)public Constructor<?>[] getConstructors():获取当前字节码文件对象中(正在运行的这个类)里面所有的公共的构造方法所在的对象
	2)public Constructor<?>[] getDeclaredConstructors():获取所有的构造器对象Constructor,返回构造器对象数组  包含私有化构造/默认的
	3)public Constructor<T> getDeclaredConstructor(类<?>... parameterTypes) :参数都是参数类型的字节码文件对象
	4)public Constructor<T> getConstructor(Class<?>... parameterTypes) :
		( ... :jdk5以后 可变参数 (参数数量未知) )
        获取指定的公共的单个的构造器对象    参数:需要书写的是当前参数类型的字节码文件对象
        
    public void setAccessible(boolean flag)参数为true,取消Java语言访问检查
    public T newInstance(Object... initargs): 参数为最终赋值的实际参数 (可变参数:实际参数未知)
/*
	现在需要使用反射(底层)如何创建p对象的!
 */
public class ReflectDemo {
    public static void main(String[] args) throws
            ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //之前的写法
        Person p = new Person() ;
        System.out.println(p);
        System.out.println("-----------------------------------------");

        //使用反射的方式创建当前类实例
        //1)获取Person类的字节码文件对象
        Class personClass = Class.forName("com.qf.reflect_03.Person") ;//class com.qf.reflect_03.Person

        //2)获取构造器对象
        //Class类
        //public Constructor<?>[] getConstructors():获取当前字节码文件对象中(正在运行的这个类)里面所有的公共的构造方法所在的对象
     
        Constructor[] constructors = personClass.getConstructors();
        for (Constructor constructor : constructors) {
            
            System.out.println(constructor);//public com.qf.reflect_03.Person()
        }
        
         //public Constructor<?>[] getDeclaredConstructors():获取所有的构造器对象Constructor,返回构造器对象数组  包含私有化构造/默认的
        Constructor[] constructors = personClass.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
            //结果:
            //private com.qf.reflect_03.Person(java.lang.String,int,java.lang.String)
            //com.qf.reflect_03.Person(java.lang.String,int)
            //public com.qf.reflect_03.Person()
        }

       //获取某一个构造器对象Constructor
        //public Constructor<T> getConstructor(Class<?>... parameterTypes) :// ... :jdk5以后 可变参数 (参数数量未知)
        //获取指定的公共的单个的构造器对象    参数:需要书写的是当前参数类型的字节码文件对象
        Constructor constructor = personClass.getConstructor();//java.lang.String

        //通过构造器对象创建当前类的实例
        //public T newInstance(Object... initargs): 参数为最终赋值的实际参数 (可变参数:实际参数未知)
        Object obj = constructor.newInstance();
        System.out.println(obj); //p
    }
}
public class ReflectDemo2 {
    public static void main(String[] args) throws Exception {
        //之前的写法
//        Person p  = new Person("高圆圆",20) ;  //默认修饰符只能同一个包下访问

        //反射创建p对象
        //1)获取类的字节码文件对象
        Class clazz = Class.forName("com.qf.reflect_03.Person") ;

        //2)获取构造Constructor(构造方法所在对象)
        //Person(String name,int age)
        获取指定的构造函数对象   ,返回值都是单个构造函数
        //public Constructor<T> getDeclaredConstructor(类<?>... parameterTypes) :参数都是参数类型的字节码文件对象
        // 公共的构造函数对象:public Constructor<T> getConstructor(Class<?>... parameterTypes)

        Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);
        //通过构造器创建当前类的实例
        //getDeclaredConstructor
        //java.lang.IllegalAccessException: Class com.qf.reflect_04.ReflectDemo2  非法访问异常:因为当前构造函数式默认修饰符
        // can not access a member of class com.qf.reflect_03.Person with modifiers ""
        //取消Java语言访问检查(反射中使用到)
        //public void setAccessible(boolean flag)参数为true,取消Java语言访问检查
        constructor.setAccessible(true);

        Object obj = constructor.newInstance("高圆圆", 20);
        System.out.println(obj);//Person{name='高圆圆', age=20, address='null'}
    }
}
3.成员变量
获取方法:
       public Field[] getFields()throws SecurityException:	获取所有的公共的成员变量Field
       public Field[] getDeclaredFields(): 所有的字段所在的Field对象,这包括公共,受保护,默认(包)访问和私有字段,但不包括继承的字段。
       
       public Field getField(String name):参数为字段名称,获取单个成员变量
       public Field getDeclaredField(String name):  获取指定的单个的字段所在的Field对象 , 参数为"当前字段名称",这包括公共,受保护,默认(包)访问和私有字段,但不包括继承的字段。
public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        //没有使用反射之前
        Person p = new Person() ;
        System.out.println(p);
        p.age = 20 ;//对象名.属性;
        System.out.println(p);

        System.out.println("-----------------------------");

        //反射的方式
        //1)获取Person类的字节码文件
        Class clazz = Class.forName("com.qf.reflect_03.Person") ;

        //方式1:直接获取当前类的实例
        //public T newInstance()
        Object obj = clazz.newInstance();
        System.out.println(obj);

        //方式2:通过获取构造函数对象,创建当前类实例
      /*  Constructor constructor = clazz.getConstructor();
        Object obj = constructor.newInstance();
        System.out.println(obj);*/

        //2)Class类

        //public Field[] getFields()throws SecurityException    获取所有的公共的成员变量Field
        Field[] fields = clazz.getFields() ;
         for (Field field : fields) {
            System.out.println(field);
            //public int com.qf.reflect_03.Person.age
        }
        
        //public Field[] getDeclaredFields():所有的字段所在的Field对象
        // 这包括公共,受保护,默认(包)访问和私有字段,但不包括继承的字段。
        Field[] fields = clazz.getDeclaredFields() ;
        for (Field field : fields) {
            System.out.println(field);
            //private java.lang.String com.qf.reflect_03.Person.name
           // public int com.qf.reflect_03.Person.age
           // java.lang.String com.qf.reflect_03.Person.address
        }

        //现在访问name   private String name ;
        //public Field getDeclaredField(String name):获取指定的单个的字段所在的Field对象
        //参数为"当前字段名称"
        Field nameField = clazz.getDeclaredField("name"); //默认:null

        //Field提供方法
        //public void set(Object obj,Object value):给绑定在当前对象上的字段进行赋值
        //参数1:创建当前类的实例
        //参数2:value 赋值的实际参数
        //取消Java语言访问检查
        nameField.setAccessible(true);
        //赋值
        nameField.set(obj,"高圆圆");
        System.out.println(obj);

        System.out.println("-----------------------------------------------");

        //public int age ;
        //获取当前age所在Field对象
        //public Field getField(String name):参数为字段名称
        Field ageField = clazz.getField("age");
        //直接赋值
        ageField.set(obj,20);
        System.out.println(obj);

        System.out.println("-----------------------------------------");
        //  String address ; //地址 默认修饰符
        //获取Field对象
        Field addressField = clazz.getDeclaredField("address");
        //取消Java语言访问检查
        addressField.setAccessible(true);
        //赋值
        addressField.set(obj,"西安市");
        System.out.println(obj);
    }
}
4.成员方法
如何通过反射获取成员方法所在的Method,并能去调用


      1)public Method[] getMethods(): 获取所有的成员方法:公共的(包含他父类的所有的公共的方法)
      2)public 方法[] getDeclaredMethods(): 通过此表示类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法
      3)public Method getMethod(String name,Class<?>... parameterTypes)
        	参数1:方法名
        	参数2:当前这个方法所携带的参数类型的Class
      4)public Method getDeclaredMetho(String name,Class<?> ..parameterTypes):  获取指定的单个的字段所在的成员方法 , 参数为"当前字段名称",这包括公共,受保护,默认(包)访问和私有字段,但不包括继承的字段。
	
	public Object invoke(Object obj,Object... args):将方法的实际参数绑定在当前类的实例上
public class ReflectDemo {
    public static void main(String[] args)  throws  Exception{
        //没有使用反射之前
        Person p = new Person() ;
        p.show();

        System.out.println("--------------------------");

        //直接反射方式获取
        //1)获取当前Person字节码文件对象
        Class clazz = Class.forName("com.qf.reflect_03.Person") ;
        Object obj =clazz.newInstance() ;//当前类实例

        //2)获取所有的成员方法:公共的(包含他父类的所有的公共的方法)
        //public Method[] getMethods()
        //public 方法[] getDeclaredMethods():通过此表示类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。
//        Method[] methods = clazz.getMethods();
       /* Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }*/

       //获取指定的方法 调用public void show()
       // public Method getMethod(String name,Class<?>... parameterTypes)
        //参数1:方法名
        //参数2:当前这个方法所携带的参数类型的Class
        Method showMethod = clazz.getMethod("show");
        //Method:底层调用方法
        //public Object invoke(Object obj,Object... args):将方法的实际参数绑定在当前类的实例上
        //参数1:就是当前类的实例
        //参数2:可变参数, 方法传递实际参数  若无参,则实际不用传递
        //返回值:如果当前方法本身有返回值,则返回Object

        showMethod.invoke(obj) ;

        System.out.println("--------------------------------------");
        //private  String functioin(String str){
        //        return str ;
        //    }

        //调用Person类中function
        //获取function方法的Method对象
        //public Method getDeclaredMetho(String name,Class<?> ..parameterTypes).
        Method functonMethod = clazz.getDeclaredMethod("functioin", String.class);
        //取消Java语言访问检查:私有方法
        functonMethod.setAccessible(true);
        Object returnObj = functonMethod.invoke(obj, "hello,洪学佳,别睡了...");
        System.out.println(returnObj);

        // void method(String message,int num){
        //        System.out.println(message+num);
        //    }
        //
        //    public int method2(){
        //        return 100 ;
        //    }
    }
}
5.应用
/*
 * 反射的应用:
 *      现在有一个ArrayList<Integer>,里面有一些元素,
 *                  如何给ArrayList添加String类型的元素呢?
 */
public class ReflectTest {
    public static void main(String[] rgs) throws Exception {

        //有一个ArrayList集合对象
        ArrayList<Integer> arrayList  = new ArrayList<>() ;
        arrayList.add(100) ;
        arrayList.add(50) ;
        arrayList.add(200) ;
        System.out.println(arrayList);

       // arrayList.add("hello") ;

        //1)获取当前集合实例所在的字节码文件对象
        //通过Object的getClass()获取当前实例所在的类的字节码对象
        Class clazz = arrayList.getClass();

        //System.out.println(obj);
        //System.out.println(clazz);//class java.util.ArrayList
        //获取当前类中 add方法所在的Method
        //public boolean add(E e)  ://参数就是任意Java类型 (Element)
        Method addMethod = clazz.getMethod("add", Object.class);
        //调用方法
        addMethod.invoke(arrayList, "hello");
        addMethod.invoke(arrayList, "world");
        addMethod.invoke(arrayList, "javaEE");

        System.out.println(arrayList);
    }
}
需求;
       现在有一个学生类以及工人类,两类都有一个love方法
       ReflectTest2在测试类中进行测试
 
       1)起初,创建的一个学生对象,调用love方法
       2)代码更改,创建一个工人类对象,调用love方法

       如何将上面的代码进行优化!
 
       Java设计原则:
               开闭原则:对修改关闭,对扩展开放 (在现有代码进程上,想办法进行扩展...)
 
               如果提供配置文件,以后只需要修改配置文件中内容,而不更改当前的代码!

               如果能将配置文件,如果能加载属性集合列表中
               className=com.qf.reflect_06.Worker
               methodName=love
 
               就可以通过key--->value
 
               com.qf.reflect_06.Worker:创建当前类的字节码文件对象
/*
 * 通过反射获取Person类的字节码文件对象并获取当前构造函数(带三个参数的构造函数)创建当前类实例
 *
 *  private Person(String name,int age,String addrss)
 */
public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        //获取Person类的字节码文件对象
        Class clazz  = Class.forName("com.qf.reflect_03.Person");

        //获取构造器对象
        //public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
        Constructor con = clazz.getDeclaredConstructor(String.class , int.class , String.class) ;

        //取消Java语言访问检查
        con.setAccessible(true);
        //通过构造器对象创建当前类实例
        Object obj = con.newInstance("高圆圆", 20, "西安市");
        System.out.println(obj);  //Person{name='高圆圆', age=20, address='西安市'}
    }
}
/*
	字节码文件:myClass.properties
		className=com.qf.reflect_06.Worker
		methodName=love
*/

/*
    工人类
 */
public class Worker {
    public void love(){
        System.out.println("爱生活,爱drink...,爱足球");
    }
}
/*
	学生类
 */
public class Student {
    public void love(){
        System.out.println("爱学习,爱Java,爱高圆圆...");
    }
}

public class ReflectTest2 {
    public static void main(String[] args) throws Exception {

        //1)现在学生类对象
       /* Student s1 = new Student() ;
        s1.love();*/

        //需求改变:创建工人类对象
        /*Worker worker  = new Worker() ;
        worker.love();*/

        //优化:提供src(类路径下提供配置文件)myClass.properties
        //1)读取src下面的myClass.properties
        //获取资源文件所在的输入流对象
        InputStream inputStream = ReflectTest2.class
                .getClassLoader().
                getResourceAsStream("myClass.properties");

        //创建属性集合列表:空的
        Properties prop = new Properties() ;
        //将指定的配置文件所在的输入流加载属性列表中
        prop.load(inputStream);
        System.out.println(prop);

        //可以通过可以key获取value
        String className = prop.getProperty("className") ; //当前类的全限定名称
        String methodName = prop.getProperty("methodName") ;

        //通过反射创建当前类的字节码文件对象
        Class clazz = Class.forName(className) ;
        //创建当前类的实例 (解耦)
        Object obj = clazz.newInstance() ;
        //通过clazz字节码文件对象获取当前成员方法所在的Method类对象
        Method method = clazz.getMethod(methodName) ;
        method.invoke(obj) ;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值