• Class对象的创建与使用


    类与Class对象


    类是程序的一部分,每个类都有一个Class对象,即每当编写并且编译一个新类的时候就会产生一个Class对象。当程序创建第一个对类的静态成员的引用的时候,会将该类动态加载到JVM中,这个说明了类的构造起器也是一个静态方法,即使在构造器之前并没有使用static关键字。所以java程序在运行之前并没有被完全加载,各个类只在需要的时候才将该类的Class对象载入内存,该Class对象被用来创建这个类的所有对象。通过下面的代码可以证明以上内容:

    class Demo1 {
        static int i;
        static {
            System.out.println("loading Demo1");
        }
    }
    class Demo2 {
        static {
            System.out.println("loading Demo2");
        }
    }
    class Demo3 {
        static {
            System.out.println("loading Demo3");
        }
    }
    class TestDemo {
        public static void main(String[] args) {
            int i = Demo1.i;
            try {
                Class.forName("Demo2");
            } catch (ClassNotFoundException e) {
                System.out.println("couldn't find Demo2");
            }
            new Demo3();
        }
        /* output
         * loading Demo1
         * loading Demo2
         * loading Demo3
         */
    }

    其中static{}是静态块,在类被加载的时候会执行,第一个输出是我们是用Demo1中的静态成员i而加载了Demo1类,而第二个输出我们调用了Class类的一个静态方法forName,参数是一个类的名称,返回的是该类名的类的Class对象,该方法还有一个作用就是若该类未被加载则加载它,最后使用了new关键字创建对象即调用了类的构造器,也对类进行了加载输出了第三行

    Class对象的创建


    若我们想要在运行的时候获取某个类的类型信息,就必须先获得该类的Class对象。得到Class对象的方法主要有三种

    • Class.forName:Class类的一个静态方法,传入类的全名
    • 对象.getClass:根类Object的方法,返回该对象的类的Class对象
    • 类名.class:这种方法又称为类字面常量,该方法不仅简单,而且更安全,因为可以在编译时就会受到检查
      class Demo1 {
          static final int i1 = 47;
          static final int i2 = (int)(Math.random() * 10000);
          static {
              System.out.println("loading Demo1");
          }
      }
      class TestDemo {
          public static void main(String[] args) {
              Class<Demo1> demo1Class = Demo1.class;
              System.out.println("after Demo1.class");
              int i = Demo1.i1;
              System.out.println("after Demo1.i1");
              i = Demo1.i2;
              System.out.println("after Demo1.i2");
          }
          /* output
           * after Demo1.class
           * after Demo1.i1
           * loading Demo1
           * after Demo1.i2
           */
      }

      从以上的代码中你会发现,通过使用类名.class的方法并没有对类进行加载,因为通过这种方法创建Class对象不会自动地初始化该Class对象。当我们使用某个类的时候实际上可以分为三个步骤:1)加载,这是由类加载器执行的,即通过查找到的字节码创建一个Class对象。2)链接,验证类中的字节码,为静态域分配存储空间,解析对其它类的引用。3)初始化,若该类有父类,则对其初始化,执行静态初始化器和静态块。从这三个步骤中看出只有对Class对象进行初始化才执行静态块。接着我们又调用了Demo1的i1也为执行静态块,因为被static final修饰的是一个编译期常量,当我们读取这个值的时候并不需要的类进行初始化,但并不是说访问的域被static final修饰时就不会对类进行初始化,从调用i2就可以看出,因为i2的值不是一个编译器的常量。

    Class对象的使用


    Class对象中提供了大量的方法来让我们获取类中的属性与方法,而且我们也可以通过Class对象来创建类的实例与修改属性值和执行方法,以下为Class对象中比较常用的方法:

    • getFields:获取public修饰的所有属性,返回一个Field数组(包括父类的)
    • getDeclaredFields:获取所有属性,返回一个Field数组
    • getField:传入一个参数(属性名),获取单个属性,返回一个Field对象,只能获取public修饰的
    • getDeclaredField:传入一个参数(属性名),获取单个属性,返回一个Field对象
      public class Demo {
          public int field1;
          private String field2;
          public void method1(Integer arg0) {
              System.out.println("执行method1");
          }
          private String method1() { return null;}
      }
      class TestDemo {
          public static void main(String[] args) throws Exception {
              Class<Demo> demoClass = Demo.class;
              Field[] fields1 = demoClass.getFields();
              Field[] fields2 = demoClass.getDeclaredFields();
              System.out.println(Arrays.toString(fields1));
              System.out.println(Arrays.toString(fields2));
              Field field1 = demoClass.getField("field1");
              // Field field2 = demoClass.getField("field2"); // 运行时抛异常
              Field field3 = demoClass.getDeclaredField("field2");
              System.out.println(field1);
              System.out.println(field3);
          }
          /* output
           * [public int Demo.field1]
           * [public int Demo.field1, private java.lang.String Demo.field2]
           * public int Demo.field1
           * private java.lang.String Demo.field2
           */
      }
    • getMethods:获取所有的public修饰的方法,包括父类的,返回Method数组
    • getDeclaredMethods:获取所有的返回,不包括父类,返回Method数组
    • getMethod:传入一个参数(方法名),返回一个Method对象,只能获取到public修饰的
    • getDeclared:传入一个参数(方法名),返回一个Method对象
      class TestDemo {
          public static void main(String[] args) throws Exception {
              Class<Demo> demoClass = Demo.class; //上段代码的Demo类
              Method[] methods1 = demoClass.getMethods();
              Method[] methods2 = demoClass.getDeclaredMethods();
              System.out.println(Arrays.toString(methods1));
              System.out.println(Arrays.toString(methods2));
              Method method1 = demoClass.getMethod("method1", new Class[]{Integer.class});
      //        Method method2 = demoClass.getMethod("method2");
              Method method3 = demoClass.getDeclaredMethod("method2");
              System.out.println(method1);
              System.out.println(method3);
          }
          /**
           * [public void Demo.method1(java.lang.Integer), public final void java.lang.Object.wait() throws java.lang.InterruptedException,...
           * [public void Demo.method1(java.lang.Integer), private java.lang.String Demo.method2()]
           * public void Demo.method1(java.lang.Integer)
           * private java.lang.String Demo.method2()
           */
      }
    • newInstance:创建该类型的一个实例
      class TestDemo {
          public static void main(String[] args) throws Exception {
              Class<Demo> demoClass = Demo.class;
              Demo demo = demoClass.newInstance();
              Field field2 = demoClass.getDeclaredField("field2");
              field2.setAccessible(true);
              field2.set(demo, "setField2");
              System.out.println(field2.get(demo));
              Method method1 = demoClass.getMethod("method1", Integer.class);
              method1.invoke(demo, new Object[]{11});
          }
          /**
           * setField2
           * 执行method1
           */
      }

      以上代码中可以看出创建类的一个实例并不只能通过new关键字来创建,而且上述还使用了Field对象的方法,可以获取一个实例中的属性值。而且你会发现通过Field对象的方法甚至可以改变一个实例的私有的属性值。若想改变私有属性值必须调用setAccessible方法并传入true(默认为false)。后面又使用了Method对象的方法,可以执行实例的方法,传入参数分别为实例与方法的参数数组,若调用Method的setAccessible方法并传入true,可以执行实例的私有方法。到这你可能会想有没有什么办法可以阻止我们通过反射(即Field和Method方法)调用那些私有的属性,可以试着将该属性值放在一个私有内部类中或则放在匿名类中,最后将会发现这都不法阻止反射的调用。但我们可以通过将一个属性用final来修饰,即使可以执行修改操作但并不会真正的改变属性值。

  • 相关阅读:
    Python股票分析系列——系列介绍和获取股票数据.p1
    快速相关
    特别长序列的快速卷积
    长序列的快速卷积
    快速卷积
    素因子快速傅里叶变换
    用一个N点复序列的FFT同时计算两个N点实序列离散傅里叶变换
    实序列快速傅里叶变换(二)
    实序列快速傅里叶变换(一)
    java 对于手机号码、邮箱、银行卡号脱敏一条龙服务
  • 原文地址:https://www.cnblogs.com/guohaien/p/7350331.html
Copyright © 2020-2023  润新知