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来修饰,即使可以执行修改操作但并不会真正的改变属性值。

时间: 2024-11-03 21:53:11

Class对象的创建与使用的相关文章

JavaScript日期时间对象的创建与使用(三)

时钟效果一: 代码: <html> <head> <meta charset="utf-8"/> <title>JavaScript日期时间对象的创建与使用</title> </head> <body> <h2 id="time"></h2> <script type="text/javascript"> function Cl

ajax之请求对象的创建以及它与服务器之间的4次“握手”(好吧其实只有3次)

request对象的创建 对于request对象的创建,我们总会第一时间反映到下列语句 var request=new XMLHttpRequest(); 不过因为各大浏览器之间的相互斗殴行为,事情是不会这么简单的. 比如非常经典的:IE浏览器没有XMLHttpRequest函数. 但这难不倒我们聪明机智的程序员,我们也有经典的应对措施: function createRequest(){     try{         request=new XMLHttpRequest();     }c

对象的创建

创建一个类,实际上是定义了一种新的复合数据类型.声明该类的一个变量,就是声明该类的对象过程.创建对象包括对象的声明和实例化两步. 1.对象的声明 对象的声明主要是声明该对象是哪个类的对象,语法如下: 类名 变量名列表: 注:变量名列表可包含一个对象名或多个对象名,如果含有多个对象名,对象名之间采用逗号分隔开.当声明一个对象时,就为该对象名在栈内存中分配内存空间,此时它的值为null ,表示不指向任何对象. 2.对象的创建 在声明对象时,并没有为该对象在堆内存中分配空间,只有通过new 操作才能完

Javascript我学之五对象的创建与使用

本文是金旭亮老师网易云课堂的课程笔记,记录下来,以供备忘. 对象的创建 JavaScript对象有两种类型 1).Native:在ECMAScript标准中定义和描述,包括JavaScript内置对象(数组,日期对象等)和用户自定义对象. 2).Host:在主机环境(如浏览器)中实现并提供给开发者使用,比如windows对象和所有的DOM对象 对象的含义 Object is an unordered collection of properties each of which contains

JVM学习:对象的创建和内存分配

1.对象的创建 java是面向对象的语言,因此对象的创建无时无刻都存在.在语言层面,使用new关键字即可创建出一个对象.但是在虚拟机中,对象创建的创建过程则是比较复杂的. 首先,虚拟机运到new指令时,会去常量池检查是否存在new指令中包含的参数,比如new People(),则虚拟机首先会去常量池中检查是否有People这个类的符号引用,并且检查这个类是否已经被加载了,如果没有则会执行类加载过程. 在类加载检查过后,接下来为对象分配内存当然是在java堆中分配,并且对象所需要分配的多大内存在类

图解java对象的创建过程

前面几篇博文分别介绍了JAVA的Class文件格式.JVM的类加载机制和JVM的内存模型,这里就索性把java对象的创建过程一并说完,这样java对象的整个创建过程就基本上说明白了(当然你要有基础才能真正看明白).经常有人问我为什么这么喜欢钻研底层的东西,首先,因为我以前的做硬件的和嵌入式的,兴趣使然:其次,我个人感觉,如果不把上下打通,心里老是有一堵墙过不去,说白了,这是个人因素,与好坏无关(当然,经常有人说,懂底层原理是成为高手的必经之路). 现在来说一下我当初学习JVM的原因,在学习JAV

.NET对象的创建、垃圾回收、非托管资源的手动处理

本篇用来梳理对象的创建.垃圾的回收,以及非托管资源的手动处理. →首先运行应用程序,创建一个Windows进程. →CLR创建一块连续的虚拟地址空间,这个地址空间就是托管堆.而且,这个地址空间最初并没有对应的物理存储空间. 虚拟地址空间分成2段.一个区段是普通堆,也叫GC堆,大小小于85000字节的引用类型对象的实例被分配在这里:另一个是大对象堆,大小大于等于85000字节的引用类型对象的实例被分配在这里. 对于客户端应用程序,每个区段的大小大致是16MB:对于服务端应用程序,每个区段的大小大致

JVM中对象的创建

对象的创建 加载class 虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载.解析和初始化过.如果没有,那必须先执行相应的类加载过程, 分配内存 在类加载检查通过后,接下来虚拟机将为新生对象分配内存.对象所需内存的大小在类加载完成后便可完全确定,为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来,有两种分法(选择哪种分配方式由Java堆是否规整决定,而Java堆是否规整又由所采用的垃圾收集器是

【JavaScript】【学习】对象的创建和继承

下午刚刚看完了<JavaScript高级程序设计>中的第六章,面向对象的程序设计,因为自己以前没有面向对象程序设计的基础,所以理解得有些困难,但是通过自己的努力研读+上网查资料+反复实践,总算是答题上理解了,对我的编程思维算是一个很大的提高吧,这里把学习笔记和心得发一下,方便以后自己查阅. 一.理解对象 在JavaScript中,一切皆是对象,前面学习引用类型,基本都是JavaScript中的内置对象,而基本类型,则都是这些内置对象的实例,BOM,DOM也是对象,全局变量可以视为window的

不允许在单例对象中创建Srping容器

spring.net在使用的时候,不允许在单例对象中创建Srping容器 需要将实例化模式转为单例singleton=“false”