java反射学习笔记

java反射学习笔记,第1张

java反射学习笔记

案例:美团外卖 --> 付款 --> 要么微信支付,要么支付宝支付

      #那么这里由美团制定规则

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

在编译后产生字节码文件的时候,类加载器子系统通过二进制字节流,负责从文件系统加载class文件。在执行程序(java.exe)时候,将字节码文件读入jvm中-->这个过程叫做类的加载。然后在内存中对应创建一个java.lang.Class对象-->这个对象会被放入字节码信息中,这个class对象对应加载那个字节码3信息,这个对象被做为程序访问方法区中的这个类的各和数据的外部接口。

所以:我们可以通过这个对象看到类的结构,这个对象是一面镜子,透过镜子看到类的各种信息,我们形象的称之为反射,这种”看透”class能力被称为introspection(内省,内现,反省)。Reflection和instrospection是常被提的两个术语。

说明:在运行期间,如果我们要产生某个类的对象,java虚拟机(jvm)会检查该类型的class对象是否已被加载。

如果没有加载,jvm会根据类的名称找到.class文件并加载它。一旦某个类型的class对象已经被加载到内存中,就可以用它来产生该类型的所有对象。

面向对象思维:万物皆对象, 将“类”当成一个对象,Class将这个“类”的抽象成对象,

想要获取到属性:class类--> 得到具体的实例(对象) --> 通过对象得到属性

提前准备下面这几个类、接口,注解

public class AliPay implements Mtwm {
    @Override
    public void payonline() {
        System.out.println("我已经点了外卖,正在使用支付宝支付");
    }
}
public class Wechat implements Mtwm {
    @Override
    public void payonline() {
        // 具体实现微信支付的功能
        System.out.println("我已经点了外卖,正在使用微信支付");
    }
}
public interface Mtwm {
    void payonline();
}
// 处定义的接口
public interface MyInterface {
    void myMethod();
}
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value();
    // 属性
}
public class Person implements Serializable {
    private int age;
    public String name;
    
    private void eat(){
        System.out.println("Person --- eat");
    }
    public void sleep(){
        System.out.println("Person --- sleep");
    }
}
@MyAnnotation(value = "hello")
public class Student extends Person implements MyInterface {
    // 属性
    private int sno;
    double height;
    protected double weight;
    public double score;
    // 方法
    @MyAnnotation(value = "method")
    public String showInfo(){
        return "我是一名三好学生";
    }
    public String showInfo(int a, int b){
        return "重载---我是一名三好学生";
    }
    
    private void work(){
        System.out.println("我以后会找工作 --> 成码农");
    }

    private void work(int a){
        System.out.println("重载 === 我以后会找工作 --> 成码农");
    }

    // 构造器
    private Student(int sno) {
        this.sno = sno;
    }
    public Student() {
    }
    Student(int sno, double weight) {
        this.sno = sno;
        this.weight = weight;
    }

    protected Student(int sno, double weight, double hight){
        this.sno = sno;
        this.weight = weight;
        this.height = hight;
    }

    void happy(){
        System.out.println("做人最重要的就是开心");
    }

    @Override
    @MyAnnotation(value = "helloMyMethod")
    public void myMethod() throws RuntimeException{
        System.out.println("我重写了myMethod方法");
    }

    protected int getSno(){
        return sno;
    }

    @Override
    public String
    toString() {
        return "Student{" +
                "sno=" + sno +
                ", height=" + height +
                ", weight=" + weight +
                ", score=" + score +
                '}';
    }
}

引入:我们在点外卖的时候选择支付方式,可以是微信,支付宝,或者是美团支付,如果是常规的写法,通过判断来确定是new那一个对象。如果说后期我需要引入其它的支付方式来进行支付,那我们又要加入其它的判断来扩展其它的支付方式。那这里程序的扩展性就不好

public class TestReflect {

    public static void main(String[] args) {
        // 定义一个字符串用来模拟前台的支付方式
        int i = 1;
        if (i == 1) {
//            new Wechat().payonline();
            pay(new Wechat());
        } else {
//            new AliPay().payonline();
            pay(new AliPay());
        }
    }
//    public static void pay(Wechat wc){
//        wc.payonline();
//    }
//    public static void pay(AliPay ap) {
//        ap.payonline();
//    }
    // 方法的形参是接口,具体传入的是接口中的实现类的对象 --> 多态的一种体现
    public static void pay(Mtwm m){
        m.payonline();
    }
}
// 多态确实可以提高代码的扩展性,但是:扩展性没有达到最好.
// 怎么没有达到最好:上面的分支,还是需要手动的删除或者添加
// 解决办法: 反射机制

上面的代码我们通过反射来做,可以明显的看出来我们只需通过修改以对应class的路径就可以new出不同的对象,这样我们的代码就不用修改,后期扩展比较方便

public class TestReflect2 {
    public static void main(String[] args) throws Exception{
        // 字符串,实际上:就是微信类的全限定路径
        String str = "com.mashibing.sync.entity.Wechat";

        //下面的代码 就是利用反射
        Class cls = Class.forName(str);
        Object o = cls.newInstance();
        Method method = cls.getMethod("payOnline");
        method.invoke(o);
    }
}

--------------------------------------------------------------------------------------------

4种获取字节码的方式

public class TestReflect3 {
    // 案例: 以person的字节码信息为案例
    public static void main(String[] args) throws Exception{
        Person p = new Person();
        Class c1 = p.getClass();
        System.out.println(c1);
        // class com.mashibing.sync.entity.Person
        Class c2 = Person.class;
        System.out.println(c2);
        // 用的最多的
        Class c3 = Class.forName("com.mashibing.sync.entity.Person");
        System.out.println(c3);
        // 利用类加载器(了解)
        ClassLoader classLoader = TestReflect3.class.getClassLoader();
        Class c4 = classLoader.loadClass("com.mashibing.sync.entity.Person");
        System.out.println(c4);
    }
}

Class类的具体的实例

  1. 类,外部类,内部类
  2. 接口
  3. 注解
  4. 数组
  5. 基本数据类型
  6. Void
public class TestReflect4 {
    public static void main(String[] args) {
        Class c1 = Person.class;
        Class c2 = Comparable.class;
        Class c3 = Override.class;
        int[] arr1 = {1, 2, 3};
        Class c4 = arr1.getClass();
        int[] arr2 = {2, 3, 4};
        Class c5 = arr2.getClass();
        // 同一个维度的数组,同一个元素类型,得到的字节码就是同一个
        System.out.println(c4 == c5);

        Class c6 = int.class;
        Class c7 = void.class;
    }
}

获取对象的构造器

public class TestReflect5 {
    public static void main(String[] args) throws Exception {
        // 获取字节码信息
        Class cls = Student.class;
        // 通过字节码信息获取构造器
        // 只能获取当前运行时类的被public修饰的构造器
        Constructor[] constructors = cls.getConstructors();
        for (Constructor c: constructors) {
            System.out.println(c);
        }
        System.out.println("----------------------------------------");
        // 获取到所有的构造器
        Constructor[] declaredConstructors = cls.getDeclaredConstructors();
        for (Constructor d: declaredConstructors) {
            System.out.println(d);
        }
        System.out.println("------------------------------------------");
        // 什么都不传入,获取到的是空参构造器, 只能获取到 public修饰的
        Constructor c1 = cls.getConstructor();
        System.out.println(c1);
        System.out.println("-----------------------------------------");
        // 获取到两个参数的构造器, 可以获取到private, protected修饰的
        Constructor c2 = cls.getDeclaredConstructor(int.class, double.class);
        System.out.println(c2);

        // 有了构造器以后,我们就可以创建对象了
        Object o = c1.newInstance();
        System.out.println(o);

        // 调用非空参构造
        Object o1 = c2.newInstance(10, 12D);
        System.out.println(o1);
    }
}

获取对象属性,及属性结构

public class TestReflect6 {
    public static void main(String[] args) throws Exception {
        Class cls = Student.class;
        // 获取到属性, 可以获取到当前类与父类中被public修饰的属性
        Field[] fields = cls.getFields();
        for (Field f: fields){
            System.out.println(f);
        }

        System.out.println("--------------------------");
        // 获取到所有的属性, 当前类中所有的属性,不包括父类中的所有属性
        Field[] f1 = cls.getDeclaredFields();
        for (Field f: f1) {
            System.out.println(f);
        }

        System.out.println("-----------------------------");
        // 获取到指定的属性, 只能获取到public修饰的
        Field score = cls.getField("score");
        System.out.println(score);

        System.out.println("-----------------------------");
        // 获取到被private修饰的属性
        Field sno = cls.getDeclaredField("sno");
        System.out.println(sno);

        System.out.println("-----------------------------");
        // 属性的具体结构
        // 获取修饰符 public static final String
        int modifiers = sno.getModifiers();
        System.out.println(Modifier.toString(modifiers));
        // 获取属性的数据类型
        Class clazz = sno.getType();
        System.out.println(clazz.getName());
        // 获取属性的名字
        String name = sno.getName();
        System.out.println(name);

        System.out.println("------------------------------");
        Field sco = cls.getField("score");
        Object obj = cls.newInstance();
        // 给属性赋值必须有对象
        sco.set(obj, 98);
        System.out.println(obj);
    }
}

获取对象所在的包,注解、接口

public class TestReflect7 {
    public static void main(String[] args) {
        Class cls = Student.class;
        // 获取运行时类的接口
        Class[] interfaces = cls.getInterfaces();
        for (Class c : interfaces) {
            System.out.println(c);
        }

        System.out.println("-----------------------------");
        // 得到父类的接口
        // 获取到父类的字节码信息
        Class superclass = cls.getSuperclass();
        // 得到接口
        Class[] i1 = superclass.getInterfaces();
        for (Class i : i1) {
            System.out.println(i);
        }

        System.out.println("-----------------------------");
        // 获取到运行时类所在的包
        Package aPackage = cls.getPackage();
        System.out.println(aPackage);
        System.out.println(aPackage.getName());

        System.out.println("-----------------------------");
        // 获取到运行时类的注解, 必须是运行时注解  声明:@Retention(RetentionPolicy.RUNTIME)
        Annotation[] annotations = cls.getAnnotations();
        for (Annotation a: annotations) {
            System.out.println(a);
        }
    }
}

获取对象的异常、方法以调用方法

public class TestReflect8 {
    public static void main(String[] args) throws Exception {
        Class cls = Student.class;
        // 获取于是所有的public的方法, 会包含其它所有父类中的方法
        Method[] methods = cls.getMethods();
        for (Method m: methods) {
            System.out.println(m);
        }

        System.out.println("-----------------------------");
        // 获取到 当前运行时类中的所有的方法
        Method[] declaredMethods = cls.getDeclaredMethods();
        for (Method d : declaredMethods) {
            System.out.println(d);
        }

        System.out.println("-----------------------------");
        // 获取到当前运行时类指定的方法
        Method showInfo = cls.getMethod("showInfo", int.class, int.class);
        System.out.println(showInfo);

        System.out.println("-----------------------------");
        // 获取到private修饰的方法
        Method work = cls.getDeclaredMethod("work", int.class);
        System.out.println(work);

        // 获取方法的具体的结构
        // 修饰符: 返回值类型 方法名(参数列表) throw XXXX{}
        System.out.println(work.getName());

        int modifiers = work.getModifiers();
        System.out.println(Modifier.toString(modifiers));

        System.out.println(work.getReturnType());

        Class[] parameterTypes = work.getParameterTypes();
        for (Class par: parameterTypes) {
            System.out.println(par);
        }

        System.out.println("-----------------------------");
        // 获取注解, 只能获取到运行时的注解
        Method myMethod = cls.getMethod("myMethod");
        Annotation[] annotations = myMethod.getAnnotations();
        for (Annotation a : annotations) {
            System.out.println(a);
        }

        System.out.println("-----------------------------");
        // 获取异常
        Class [] exceptionTypes = myMethod.getExceptionTypes();
        for (Class e : exceptionTypes) {
            System.out.println(e);
        }

        System.out.println("-----------------------------");
        // 调用方法
        Object o = cls.newInstance();
        // 调用o对象的myMethod方法
        myMethod.invoke(o);
        // 调用带参的方法
        System.out.println(showInfo.invoke(o, 12, 45));
    }
}

关于反射的面试题:

  1. 创建person的对象,以后用new Person()创建还是用反射创建

        需要体现程序的动态性的时候使用反射来创建对象;不知道是创建那个对象,只有在运行的时候才知道是创建那一个对象,比如说我们在点外卖的时候选择支付方式,可以微信支付、支付宝支付、或者是美团支付只有在用户选择后我们才知道去创建那个对象,这个时候我们就需要修改反射来创建对象。

  1. 反射是否破坏了面向对象的封装性

        破坏了面向对象的封装性,但是二者并不冲突。对象的封装主要是解决安全性,反射的提出主要是为了解决程序的动态性。 对象的privte修饰的属性、方法是就明确的告诉就不要使用这里的属性和方法; 但反射确实是调用到或者使用到private修饰的方法或属性。一般来说是不建议使用这里对象里面的private修饰的属性或者方法。因此这里说他们是不冲突的。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5694802.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存