黑马程序员--反射知识点总结



——- android培训java培训、期待与您交流! ———-

认识Class类

之前学习的java处理的基本机制,是先把java源文件通过java编译器编译后生成字节码文件(.class),然后通过ClassLoader机制将这些字节码文件加载到内存中,最后生成相应的实例并执行。

Class类与java反射

通过java的反射机制,可以访问程序中已经加载进内存的java对象的描述,可以访问、检测和修改描述java对象本身的信息。

Class和class的区别

1)class:Java中的类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则由此类的实例对象确定,不同的实例对象有不同的属性值。

2)Class:指的是Java程序中的各个Java类是属于同一类事物,都是Java程序的类,这些类称为Class。例如人对应的是Person类,Java类对应的就是Class。

3)属性:类名,类的访问属性,类所属包名,字段名称列表,方法名称列表等。

获取字节码的三种方式

方式一:
Object类的getClass()方法,判断两个对象是否是同一个字节码文件,对象.getClass()

方式二:
静态属性class,锁对象。类名.class是一种固定写法。源程序一看到就知道是字节码

方式三:
Class类中静态方法forName(),读取配置文件Class.forName()

public static void main(String[] args) throws ClassNotFoundException {
        Class clazz = Peason.class;//静态属性class,锁对象     获取字节码方式一

        Class clazz2 = Class.forName("TestDemo.Peason");//Class类中静态方法forName(),读取配置文件   获取字节码方式二    


        Peason  z = new Peason();
        Class clazz3 = z.getClass();//Object类的getClass()方法,判断两个对象是否是同一个字节码文件     获取字节码方式三

        System.out.println(clazz==clazz2);
        System.out.println(clazz2==clazz3);
    }

反射中的各种类:

一、Constructor类

1、概述:Constructor代表某个类的构造方法

2、获取构造方法:

1)如何得到摸个类的所有构造方法:如得到String类的所有构造方法

Constructor[] cons = Class.forName(“java.lang.String”).getConstructors();

2)获取某一个构造方法:

Constructor con =String.class.getConstructor(StringBuffer.class);①

3、创建实例对象:

1)通常方式:String str = new String(new StringBuffer (”abc”));

2)反射方式:String str = (String)con.newInstance(new StringBuffer(“abc”));②

调用获得的方法时要用到上面相同类型的实例对象,即两个StringBuffer()要对应相等。

NewInstance():构造出一个实例对象,每调用一次就构造一个对象。

注意:上面的两个地方①②都要用到StringBuffer,这必须是一致的。

第①个是指定要带StringBuffer参数类型的构造方法,即所需使用的是含StringBuffer类型的构造方法。

第②个是用这个构造方法创建对象,要传入的参数类型是StringBuffer。

4、Class.newInstance():创建一个对象,不带参数的构造方法。

        //new String(new StringBuffer("abc"));
        Constructor constructor1 = 
        String.class.getConstructor(StringBuffer.class);
        String str2 =
         (String)constructor1.newInstance(new StringBuffer("abc"));
        System.out.println(str2);

        //Class.newInstrance创建不带参数的构造方法
        String str3 = 
        (String)Class.forName("java.lang.String").newInstance();
        System.out.println("str3:"+str3);

二、Field类

1、概述:Field类代表成员变量(字段)的反射。

    public class ReflectPoint {
        private int x;
        public int y;

        public String toString(){
            return str1+";" + str2 + ";" + str3;
        }
    }
    public class FieldTest(){
    ReflectPoint pt1 = new ReflectPoint(3,5);
        //fieldX和fieldY并不是对象身上的变量,而是类上的
        //要用它去取某个对象上的对应的值,传入什么对象,就取相应对象的值。
        Field fieldY = pt1.getClass().getField("y");
        System.out.println(fieldY.get(pt1));
        //获取私有的成员变量
    Field fieldX = pt1.getClass().getDeclaredField("x");
        fieldX.setAccessible(true);
        System.out.println(fieldX.get(pt1));
    }

2、获取成员变量:

如上例子所示:

1)获取公有的成员变量:

getField(String name)和get(变量)

2)获取私有的成员变量:暴力反射

getDeclared(String name)

setAccessible(boolean b),将b设为true即可

get(变量)

//替换字符 
private static void changeStringValue(Object obj) throws Exception {
    Field[] fields = obj.getClass().getFields();
    for(Field field : fields){
        //此处需要用==比较,因为是同一份字节码对象
        if(field.getType() == String.class){
            String oldValue = (String)field.get(obj);
            String newValue = oldValue.replace('b','a');
            field.set(obj, newValue);
        }
    }
}

三、Method类

1、概述:Method类代表某个类中的一个成员方法。

调用某个对象身上的方法,要先得到方法,再针对某个对象调用。

2、专家模式:谁调用这个数据,就是谁在调用它的专家。

如人关门:

调用者:是门调用管的动作,对象是门,因为门知道如何执行关的动作,通过门轴之类的细节实现。

指挥者:是人在指挥门做关的动作,只是给门发出了关的信号,让门执行。

总结:变量使用方法,是方法本身知道如何实现执行的过程,也就是“方法对象”调用方法,才执行了方法的每个细节的。

3、获取某个类中的某个方法:(如String str = ”abc”)

1)通常方式:str.charAt(1)

2)反射方式:

Method charAtMethod=
Class.forName(“java.lang.String”).getMethod(“charAt”,int.class);

charAtMethod.invoke(str,1);

说明:如果传递给Method对象的invoke()方法的第一个参数为null,说明Method对象对应的是一个静态方法

4、用反射方式执行某个main方法:

首先要明确为何要用反射:在写源程序时,并不知道使用者传入的类名是什么,但是虽然传入的类名不知道,而知道的是这个类中的方法有main这个方法,所以可以通过反射的方式,通过使用者传入的类名(可定义字符串型变量作为传入类名的入口,通过这个变量代表类名),内部通过传入的类名获取其main方法,然后执行相应的内容。

//Method类演示
private static void methodTest(String [] args) throws Exception {
    String str1 = "abc";
    //一般方法:
    System.out.println(str1.charAt(1));
    //反射方法 :
    Method methodCharAt =
        Class.forName("java.lang.String").getMethod("charAt",int.class);
    System.out.println(methodCharAt.invoke(str1,1));

    //用反射方式执行某个main方法
    //一般方式:
    Test.main(new String[]{"aaa","bbb","ccc"});
    System.out.println("-------");

    //反射方式:
    String startingClassName = args[0];
    Method methodMain =
        Class.forName(startingClassName).getMethod("main",String[].class);
        //方案一:强制转换为超类Object,不用拆包
        methodMain.invoke(null,(Object)new String[]{"aaa","bbb","ccc"});
        //方案二:将数组打包,编译器拆包后就是一个String[]类型的整体
        methodMain.invoke(null,new Object[]{new String[]{"aaa","bbb","ccc"}});
    }
//定义一个测试类
class Test{
    public static void main(String [] args){
        for(String arg : args){
            System.out.println(arg);
        }
    }
}

四、数组的反射

1、数组字节码的名字:有[和数组对应类型的缩写,如int[]数组的名称为:[I

2、基本数据类型的一维数组不能转换为Object数组,如:

int[] a = new int[3];Object[] obj= a;这样是不成立的。

3、如何得到某个数组中的某个元素的类型:

例:int a = newint[3];Object[] obj= new Object[]{”ABC”,1};

无法得到某个数组的具体类型,只能得到其中某个元素的类型,如

Obj[0].getClass().getName()得到的是java.lang.String

若通过b.getClass().getName(),结果是:[Ljava.lang.Object;

public static void main(String[] args) {
        int[] a1 = new int[]{1,2,3,4}; 
        String[] a2 = new String[]{"a","b","c"};
        printObject(a1);
        printObject(a2);
        printObject("abc");
        }
      private static void printObject(Object obj){
          Class clazz = obj.getClass();//获取对象的字节码
          if(clazz.isArray()){      //判断是否是数组
              int len = Array.getLength(obj);//获取数组的长度
              for (int i = 0;i < len;i++) { //遍历打印数组内元素
                System.out.println(Array.get(obj,i));
            }
          }else{
              System.out.println(obj);//如果不是数组就直接打印
          }
      }
}

反射通过泛型检查

ArrayList的一个对象,在这个集合中添加一个字符串数据,如何实现呢?

public static void main(String[] args) throws Exception {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(111);
        list.add(222);

        Class<?> clazz = Class.forName("java.util.ArrayList");//获取集合的字节码对象

        Method m = clazz.getMethod("add", Object.class);//通过字节码获取方法
        Object s =  m.invoke(list,"abc");          //调用方法
        System.out.println(s);                //打印方法的返回值
        System.out.println(list);

通过反射写一个通用的设置某个对象的某个属性为指定的值

public static void setProperty(Object obj, String propertyName, Object value) throws Exception{
        Class<?> clazz = obj.getClass();//获取字节码对象
        Field f = clazz.getDeclaredField(propertyName);//暴力获取给字段的名字
        f.setAccessible(true);             //去除权限
        f.set(obj, value);   //修改属性
public static void main(String[] args) throws Exception {
        Student s = new Student();     //创建对象
        Test3.setProperty(s, "name", "王小乖");//调用方法
        Test3.setProperty(s, "age", 21);

        System.out.println(s);
    }
}
class Student{
    private String name;//将属性私有。并且不给提供访问方法
    private int age;

    public String toString(){
        return name +","+age;
    }
    }

动态代理

动态代理:在程序运行过程中产生的这个对象,而程序运行过程中产生对象其实就是反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理
代理:本来应该自己做的事情,请了别人来做,被请的人就是代理对象。

public static void main(String[] args) {
        UserImp ui = new UserImp();
        ui.add();
        ui.delete();
        System.out.println("-----------------------");


        MyInvocationHandler m = new MyInvocationHandler(ui);
        User u = (User) Proxy.newProxyInstance(ui.getClass().getClassLoader(), ui.getClass().getInterfaces(), m);
        u.add();
        u.delete();
public interface User {
    public void add();

    public void delete();
}
public class UserImp implements User {

    @Override
    public void add() {
        //System.out.println("权限校验");//通过输出类模拟
        System.out.println("添加功能");
        //System.out.println("日志记录");//<span style="font-family: Arial, Helvetica, sans-serif;">通过输出类模拟</span>

    }

    @Override
    public void delete() {
        //System.out.println("权限校验");
        System.out.println("删除功能");
        //System.out.println("日志记录");
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值