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来修饰,即使可以执行修改操作但并不会真正的改变属性值。
Class对象的创建与使用的更多相关文章
- 图解JAVA对象的创建过程
前面几篇博文分别介绍了JAVA的Class文件格式.JVM的类加载机制和JVM的内存模型,这里就索性把java对象的创建过程一并说完,这样java对象的整个创建过程就基本上说明白了(当然你要有基础才能 ...
- JVM中对象的创建过程
JVM中对象的创建过程如以下流程图中所示: 对其主要步骤进行详细阐述: 为新生对象分配内存: 内存的分配方式: 指针碰撞:假设Java堆中内存是绝对规整的,所有用过的内存放在一边,空闲的内存在另一边, ...
- 《JavaScript高级程序设计》读书笔记--(4)对象的创建
ECMAScript支持面向对象(OO)编程,但不使用类或者接口.对象可以在代码执行过程中创建或增强,因此具有动态性而非严格定义的实体.在没有类的情况下,可以采用下列模式创建对象. 对象的创建 工厂模 ...
- java 对象 :创建
灵感来自effective java 关于对象,是java的核心,如何有效的创建其实是一个值得关注的地方. 1.静态工厂:这是一个值得关注的,并且应该时刻考虑的方法. 优点:1.他是有名字的,这个是如 ...
- 深入理解Java虚拟机(二)、Java对象的创建,内存布局和访问定位
对象的创建: Object obj = new Object(); 常量池中是否有Ljava.lang.Object
- java类的初始化和对象的创建顺序
学习java编程思想--类的初始化p146 类的加载顺序* 1加载器启动找到 xxx.class文件,通过extends关键字寻找基类,先加载基类* 2类初始化先初始化static成员变量和stati ...
- Emit学习(2) - IL - 对象的创建过程
上一篇的介绍中, 并没有介绍到对象的创建过程, 这一篇主要就介绍一下, 对象的创建过程. 其实熟悉了IL语法之后, 完全可以用Reflector反编译代码去查看. 而且正因为有这个工具, 可以对照着R ...
- JavaScript对象的创建之基于构造方法+原型方式
为了解决原型所带来的问题,此处需要通过组合构造方法和原型来实现对象的创建,将属性在构造方法中定义,将方法在原型中定义.这种有效集合了两者的优点,是目前最为常用的一种方式. function Perso ...
- JavaScript对象的创建之使用json格式定义
json: javascript simple object notation. json就是js的对象,但是它省去了xml中的标签,而是通过{}来完成对象的说明. 定义对象 var person = ...
- js中对象的创建
json方式,构造函数方式,Object方式,属性的删除和对象的销毁 <html> <head> <title>js中的对象的创建</title> &l ...
随机推荐
- Order笔记-项目导入
问题: @Override报错: @Override注释在jdk1.5环境下只能用于对继承的父类的方法的重写,但不能用于对实现的接口中的方法的实现.(也就是jdk1.5的 @Override这个ann ...
- 【SqlServer】【问题收集】必须声明标量变量
1 问题概述 在DAL层,通过标量给变量赋值时,出现如下异常 我们来看看在数据访问层的SQL语句: //根据EmployeeName条件获取数据 public DataTable GetEmplo ...
- OpenCASCADE构造一般曲面
OpenCASCADE构造一般曲面 eryar@163.com Abstract. 本文主要介绍常见的曲面如一般柱面(拉伸曲面).旋转面在OpenCASCADE中的构造方法,由此思考一般放样算法的实现 ...
- sqlserver 存储过程 修改
CREATE PROCEDURE [dbo].[UpdateMessage] @strTable varchar(), --要修改的表 @strColumn varchar(),--要修改的列名(如果 ...
- 520. Detect Capital
Given a word, you need to judge whether the usage of capitals in it is right or not. We define the ...
- bzoj 3675: [Apio2014]序列分割
Description 小H最近迷上了一个分隔序列的游戏.在这个游戏里,小H需要将一个长度为n的非负整数序列分割成k+1个非空的子序列.为了得到k+1个子序列,小H需要重复k次以下的步骤: 1.小H首 ...
- Dubbo(二) 认识Zookeeper
前言 在昨天,我们给大家基本介绍了Dubbo,文中反复提到了Zookeeper,那么它到底是什么呢,这篇文章我们将从Dubbo层面去了解Zookeeper,不做全面讲解,毕竟这是Dubbo教程啊~ Z ...
- Linux下安装破解JIRA 6.3.6 并连接MYSQL5
序言 JIRA是澳大利亚 Atlassian 公司开发的一款优秀的问题跟踪管理软件工具,可以对各种类型的问题进行跟踪管理,包括缺陷.任务.需求.改进等.JIRA采用J2EE技术,能够跨平台部署.它正被 ...
- MarkDown的用法
# 一级标题## 二级标题### 三级标题#### 四级标题##### 五级标题###### 六级标题# 无序标题- 文本- 文本- 文本# 有序标题1. 文本2. 文本3. 文本# 图片链接[张驰博 ...
- Android 7.1 WindowManagerService 屏幕旋转流程分析 (三)
三.屏幕的绘制 performSurfacePlacement()函数来触发window的绘制,这里最大的循环次数是6,当然一般不会到最大次数就会被Scheduled. final void perf ...