Java 反射学习要点(一)

30 篇文章 0 订阅
13 篇文章 0 订阅

反射的概念:

    java可以在运行时动态的获取到某个类的类信息,这就是java的反射。
  • 什么叫类信息

    • 一个类的类信息包括它继承自哪个类,实现了哪些接口,有哪些属性、方法、构造方法等。
  • 什么叫类对象

    • java.lang.Class是一个类,它是反射操作的源头,这个类的对象就叫类对象。
  • 类对象都有啥东西
    • 类对象就保存了类信息。

三种获得类对象的方法

  • 调用Object类中的getClass()方法

    • 就是通过类的对象来获得类对象
    • 有实例化对象产生,再取得类对象
    • 返回值是某个对象的类对象,也就是说同一类型的实例化对象公用同一个类对象,因此,对于类对象的比较可以使用“==”
  • 使用“类名.class“获得类对象
    • 没有实例化对象产生
  • 调用Class类提供的一个静态方法forName()获得类对象
    • forName()方法需要接收一个字符串类型的数据作参数,表示是类的名字,根据类名返回一个类对象
    • 强制类加载
      • 当JVM第一次遇到一个类,就得找到它到底是个啥,就是通过CLASSPATH来找它对应的字节码文件,就是那个.class文件,找到之后就读入这个文件,这个文件里就有这个类的类信息,JVM就把这些信息保存了,这个保存就是JVM把这些信息封装到对象中去了,就像给对象纹身一样,纹的就是这个对象所属的类到底都有些啥。
    • 与第二种方法相比,可以不使用import语句导入一个明确的类,类的名称是采用字符串的形式当作参数描述的。

使用反射获得类的实例化对象

之前都是使用关键字"new"进行对象的实例化,现在如果拿到了Class类对象,就可以利用反射来进行对象的实例化操作。
Class<?> cls =Class.forName("java.util.Date");
Object obj = cls.newInstance();

这就相当于使用new关键字调用无参构造方法来实例化对象。

使用类对象来获取类的信息

我们通过上面三种获得类对象的方式获得类对象之后,就可以调用类对象中的方法来获取这个类的信息。那么类对象都可以调用哪些方法呢:
  • getName()
  • 获得类的名字,包括包名
  • getSimpleName()
  • 获得类的名字,但不包括包名
  • getSuperClass()
  • 获得本类的父类的类对象,注意是类对象
  • getInterface()
  • 获得本类所实现的所有接口的类对象,返回值是一个类对象的数组,Class[]。
public class TestClass1 {
    public static void main(String[] args) {
        Class c = ArrayList.class;//类名.class 的方式获取类对象

        String calssName = c.getName();//通过类对象获取类的名称
        System.out.println("类名:"+calssName);

        String simpleName = c.getSimpleName();//通过类对象获取类的简单名称,不包括类所在包名
        System.out.println("简单类名: "+simpleName);

        Class superClass = c.getSuperclass();//通过类对象获得本类的父类的类对象
        System.out.println("父类:"+superClass.getName());//通过父类的类对象获得父类的名称

        Class [] interfaces = c.getInterfaces();
        System.out.println("接口:");
        for(int i=0; i<interfaces.length;i++){
            System.out.println(interfaces[i].getName());//获得本类所实现的所有接口的类对象
        }

    }

}

使用类对象类获取类中方法的信息

  • getMethods[]
  • 返回的Method类型的数组中包括

    • 所有的公开方法,包括父类中的公开方法,但私有方法不能被获得
  • getDeclaredMethods

  • 也是返回一个Method数组,包括私有方法,但是不包括父类中的任何方法。

使用类对象来创建类的对象

看清楚,一定要区分这2个到底是指的什么!!!
  • newInstance()通过类的无参构造来创建对象
Class c = Student.class;
Student stu = (Student)c.newInstance();
  • 给定一个字符串参数,这个参数表示一个类的名字,根据类名来创建该类的一个实例对象
    • 接收一个类名的字符串参数,很明显是使用forName()方法
public class Dog {
    String name;
    int age;
    public Dog() {
        System.out.println("Dog()");
    }
    public Dog(String name,int age){
        System.out.println("Dog(String,int)");
        this.name=name;
        this.age=age;
    }
    @Override
    public String toString() {

        return name+" "+age;
    }

}

public class TestConstructor {
    public static void main(String[] args) throws Exception {
        Class c = Dog.class;

        //利用无参构造方法创建对象,可直接使用Class类的newInstance()方法
        Dog d1 = (Dog) c.newInstance();
        System.out.println(d1);

        //获得构造方法
        Constructor con = c.getConstructor(new Class[] {String.class,int.class});
        //创建对象时指定构造方法
        Dog d2 = (Dog) con.newInstance( //为构造方法传递参数
                new Object[]{"Snoopy",new Integer(5)});
        System.out.println(d2);
    }
}

Field 类

 先总结一下前面的内容:
   学了啥叫反射-->反射可以有3种方式来获得类对象
   拿到类对象
   -->可以使用类对象调用类对象所拥有的方法来获得:类的全名、类的简单名字、父类的类对象、所实现接口的类对象
   -->可以使用类对象获取类中的方法的信息
   -->使用类对象创建类的实例化对象

上面说到类的信息除了继承的类、实现的接口、拥有的方法等,还包括属性信息。
一个Field对象就封装了一个属性的信息。

  • 获取属性

    • getField()获得本类的公开属性及从父类继承的公开属性,没有私有属性

    • getDeclaredField() 只能获得本类的属性,包括私有属性

  • 修改、读取属性
    有了Field对象之后就可以利用反射来获取、修改属性的值。

    • 在不用反射来修改属性时,我们需要明确3个要素:一是要对象,我们要修改哪一个对象的属性;二是属性,知道是哪一个对象之后,对象属性很多,到底是修改对象的哪一个属性;三是属性的值,我们知道要哪一个对象的哪一个属性了,那要把把它改成什么值。
    • 使用反射来修改属性,同样我们也得知道这3个要素:所以第一步,获得要被修改的属性对应的Field对象;第二步,Field对象调用set方法,set(Object obj,Object value)里面的2个参数就分别表示了要修改哪个属性和把属性设置成哪个值。
  • 需要注意的是反射也可以突破私有限制,获得并修改对象的私有属性
public class Student {
    public String name;
    private int age;
    public Student(){
        System.out.println("Student()");
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    public void study(){
        System.out.println(name+ " study");
    }
    public void study(int h){
        System.out.println(name+" study for "+h+" hours");
    }
    public double study(int a,double b){
        System.out.println(name+" study "+a+" "+b);
        return a*b;
    }
    private void play(){
        System.out.println(name+" play");
    }

}
public class TestStudent {
    public static void main(String[] args) throws Exception{
        Class c = Student.class;
        Student student= (Student) c.newInstance();//无参构造方法被调用,创建了一个Student对象

        //获取特定属性
        Class c1=Student.class;
        Field nameField = c1.getField("name");//获得代表Student类的name属性的Field对象

        //利用反射来修改属性
        Student stu = new Student();
        Class c2 =stu.getClass();
        Field nameField2 = c2.getField("name");// 获取相应的Field对象,表示要修改哪一个对象的属性;要修改对象的哪一个属性,通过调用getField方法说明要修改的是属性名为name的属性
        nameField2.set(stu, "Tom");//第一个参数说明要修改属性的对象;第二个参数是属性值要修改成什么

        //获取属性 获得stu对象的name属性值
        Field f = c2.getDeclaredField("name");// 获取相应的Field对象--f,要获取对象属性名为name的属性值
        Object nameValue = f.get(stu);//利用Field类中的get方法,取到属性值,get方法的参数表明了要读取哪一个对象的属性,get的返回值则表明了读取到的属性值

        //获取私有属性,反射可以获得并修改对象的私有属性
        Field ageField = c2.getDeclaredField("age");//age属性是私有的,只能使用getDeclaredField方法
        //在读取和修改属性值之前调用一个方法
        ageField.setAccessible(true);
        ageField.set(stu, new Integer(18));//age属性是一个int类型,但是由于set方法第二个参数是Object类型,因此必须把18这个整数值封装成Integer对象

        //利用反射调用study方法
        Student stu3 = new Student();//实例化一个对象
        Class c3 =stu3.getClass();

        //确定要调用哪个方法,获得该方法所对应的方法对象
        Method m = c3.getDeclaredMethod("study", new Class[]{int.class,double.class});//获得一个代表有2个参数的study方法的Method对象,表示调用哪个方法

        Object obj //是invoke方法的返回值
        = m.invoke(stu3, //是invoke方法的第一个参数,表明要对stu3对象调用方法,就是确定对象
                new Object[]{new Integer(10),new Double(1.5)});//传入2个参数
        //也可以调用类中的私有方法,只需要在调用Method对象的invoke方法之前,先调用setAccessible(true)即可

    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值