第三章类加载和反射.docx
- 文档编号:23988658
- 上传时间:2023-05-23
- 格式:DOCX
- 页数:15
- 大小:21.88KB
第三章类加载和反射.docx
《第三章类加载和反射.docx》由会员分享,可在线阅读,更多相关《第三章类加载和反射.docx(15页珍藏版)》请在冰豆网上搜索。
第三章类加载和反射
第三章类的加载和反射
一、类的加载、连接和初始化
3.1.1JVM和类
运行某个Java程序时,将会启动一条Java虚拟机进程,不管该Java程序有多么复杂,该程序启动了多少个线程,它们都处于Java虚拟进程里。
同一个JVM的所有线程、所有变量都处于同一个进程里,它们都使用该JVM进程的内存区。
JVM进程在以下几种情况下会被终止:
程序运行到最后正常结束
程序运行到使用System.exit()或Runtime,getRuntime().exit()代码结束程序
程序执行过程中遇到未捕获的异常或错误而终止
程序所在平台强制结束了JVM程序
示例:
求第三个代码块A2类中输出的a对象的值
publicclassA{
publicstaticinta=6;
}
publicclassTestA1{
publicstaticvoidmain(String[]args){
Aa1=newA();
a1.a++;
}
}
publicclassTestA2{
publicstaticvoidmain(String[]args){
Aa2=newA();
System.out.println(a2.a);//a的值为多少
}
}
3.1.2类的加载
当程序主动使用某个类时,如果该类还未加载到内存中,系统会通过加载、连接、初始化三个步骤来对该类进行初始化。
类的加载是指将类的class文件读入内存,并为之创建一个java.lang.Class对象,也就是说当程序中使用某个类时,系统都会为之建立一个java.lang.Class对象。
类的加载由类加载器完成,类加载器由JVM提供,JVM提供的这些类加载器通常被称为系统类加载器。
PS:
类加载器通常无须等到“首次使用”该类时才加载该类,Java虚拟机规范允许系统预先加载某些类。
3.1.3类的连接
当类被加载之后,系统为之生成一个对应的Class对象,接着将会进入连接阶段,连接阶段会负责把类的二进制数据合并到JRE中。
3.1.4类的初始化
在类的初始化阶段,JVM负责对类进行初始化,主要就是对静态属性进行初始化。
可使用以下两种方法
1、声明静态属性时指定初始值
publicstaticinta=6;
2、使用静态初始化块为静态属性指定初始值
publicstaticinta;
static{
a=6;
}
JVM初始化一个类包含几个步骤:
1.假如这个类还没有被加载和连接,程序先加载并连接该类
2.假如该类的直接父类还没有被初始化,则先初始化其直接父类
3.假如类中有初始化语句,则系统依次执行这些初始化语句
3.1.5类初始化的时机
JVM初始化某个类或接口的六种方式:
1.创建类的实例:
包括使用new创建实例,通过反射创建实例以及使用反序列化创建实例
2.调用某个类的静态方法
3.访问某个类的静态属性,或为该属性赋值
4.使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。
例如:
Class.forName(“Person”).
5.初始化某个类的子类
6.直接使用java.exe命令运行某个主类
在调用某个类的静态常量时,遇到如下代码,并不会初始化该类:
publicclassDemo{
static{
System.out.println("静态初始化块语句");
}
staticfinalStringfinalConstant="类的加载和反射";
}
以下对静态常量的调用方式并不会初始化该类。
publicclassTestDemo{
publicstaticvoidmain(String[]args){
System.out.println(Demo.finalConstant);
}
}
因为对于能直接获取直的常量,在编译该类时会被直接编译成常量值,也就是"类的加载和反射"这个字符串。
PS:
当某个静态属性使用final修饰,而且它的值可以在编译时得到,那么程序其他地方使用该静态属性时,实际上并不会使用该静态属性,而是相当于使用常量。
因此,下面这种形式的常量在调用时是能初始化类的:
publicclassDemo{
static{
System.out.println("静态初始化块语句");
}
//staticfinalStringfinalConstant="类的加载和反射";
staticfinalStringfinalConstant=System.currentTimeMillis()+"";
}
因为此时finalConstant的值是动态在调用时赋值的。
3.1.6类加载机制
当使用类加载器ClassLoader加载某个类时,该类所依赖和引用的其他类也将由该加载器一并加载。
而且所有被加载过的Class都会被缓存,当程序需要使用某个Class的时候先检测缓存中有没有该Class,只有当缓存中不存在时系统才会重新读取该类对应的二进制数据,并将其转换成Class对象,并存入缓存。
这就是为什么在修改了类文件后,程序必需重新启动JVM的原因。
ClassLoadercl=ClassLoader.getSystemClassLoader();
try{
//使用加载器仅加载该类的class文件
cl.loadClass("reflect.Demo");
System.out.println("==使用Class.forName==");
//调用Class.forName()时才会对该类进行初始化
Class.forName("reflect.Demo");
}catch(ClassNotFoundExceptione){
e.printStackTrace();
}
二、通过反射查看类信息
Java中许多对象在运行时都会出现两种类型:
编译时类型和运行时类型。
如:
Personp=newStudent();这行代码将会产生一个变量p,该变量的编译时类型为Person,运行时类型为Student。
如果程序在运行时需要发现对象的真实信息,有两种做法:
1.假设在编译和运行时都完全知道具体类型则可以使用instanceof去进行判断,然后再进行强制类型转换即可。
2.如果编译时根本无法预知该对象和类可能属于哪些类,程序就只能依靠运行时信息来发现该对象和类的真实信息,这就必须使用反射。
3.2.1获得Class对象
当某个类被加载后就会生成一个java.lang.Class对象,获得该对象用三种方法:
1.使用Class类的forName(Stringstr)静态方法。
str是一个全限定的类名,即完整包名+类名。
2.调用某个类的class属性来获取Class对象。
如Person.class
3.调用某个对象的getClass()方法。
使用第二种方式最佳,因为程序在编译阶段就可以检测被调用的Class对象是否存在,而且调用属性比调用方法能达到更好的性能。
当然,如果只知道字符串形式的类名就必需得使用第一种方式获取Class对象。
3.2.2从Class中获取信息
Class类提供了大量实例方法来获取该Class对象所对应的详细信息,Class类大致包含如下几种方法:
获取构造方法:
Constructor
Constructor
Constructor
Constructor
获取方法:
MethodgetMethod(Stringname,Class
>..type):
获取所指定类型的public方法
Method[]getMethods():
获取所有的public方法
PS:
获取无关访问级别的方法则同上使用有declared的方法。
获取字段:
FieldgetFiled(Stringname):
获取所指定的public字段属性(Field)
Field[]getFields():
获取所有public字段
PS:
获取无关访问级别的属性字段则同上使用有declared的方法。
假设现有如下三个方法:
Øpublicvoidinfo()
Øpublicvoidinfo(Stringstr)
Øpublicvoidinfo(Stringstr,Integernum)
clazz.getMethod(“info”,String.class)则获取第二个方法
clazz.getMethod(“info”,String.class,Integer.class)则获取第三个方法
练习:
编写一个类,分别拥有重载的带参,不带参的构造方法(私有和公有)。
编写一个info方法,并进行重载。
测试:
输出该类所有的构造方法,所有public构造方法,所有方法,带String参数的info方法
publicclassClassTest{
privateClassTest(){
}
publicClassTest(Stringname){
System.out.println("执行有参的构造方法");
}
publicvoidinfo(){
System.out.println("执行无参的Info方法");
}
publicvoidinfo(Stringstr){
System.out.println("执行有参的info方法:
参数是:
"+str);
}
publicstaticvoidmain(String[]args)throwsException{
//获取Class对象
Class
//获取所有构造器(无关访问修饰符)
Constructor[]ctors=clazz.getDeclaredConstructors();
System.out.println("ClassTest的全部构造器如下:
");
for(Constructorconstructor:
ctors){
System.out.println(constructor);
}
//获取所有public的构造器
Constructor[]publicCtors=clazz.getConstructors();
System.out.println("\nClassTest的全部public构造器:
");
for(Constructorconstructor:
publicCtors){
System.out.println(constructor);
}
//获取所有方法
Method[]methods=clazz.getDeclaredMethods();
System.out.println("\nClassTest的全部方法:
");
for(Methodmethod:
methods){
System.out.println(method);
}
//获取带一个String参数的info方法
System.out.println("\nClassTest里带一个字符串的info方法:
"+clazz.getMethod("info",String.class));
}
}
三、使用反射生成并操作对象
3.3.1创建对象
通过反射生成对象的两种方式:
Ø使用Class对象的newInstance()方法创建对象,实际上是调用公共无参构造方法
Ø先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象所对应的实例
练习:
通过读取属性配置文件并利用反射来创建对象。
在类中定义一个私有Map
定义方法传入String类型类名,反射创建对象。
定义方法传入String类型文件名读取配置文件并加载入Properties然后获取所有值。
publicclassObjectPoolFactory{
privateMap
privateObjectcreateObject(StringclzName)throwsClassNotFoundException,Exception{
//根据传入的字符串获取class对象
Class
>clz=Class.forName(clzName);
//调用clz对应类的无参构造方法
returnclz.newInstance();
}
publicvoidinitProperties(StringfileName){
FileInputStreamfis=null;
try{
fis=newFileInputStream(fileName);
Propertiesprops=newProperties();
props.load(fis);
//循环所有属性名stringPropertyNames
for(Stringname:
props.stringPropertyNames()){
//将属性值获取并创建对象放入对象MAP
objectPool.put(name,createObject(props.getProperty(name)));
}
}catch(Exceptione){
e.printStackTrace();
}finally{
try{
if(fis!
=null){
fis.close();
}
}catch(IOExceptione){
e.printStackTrace();
}
}
}
publicObjectgetObject(Stringname){
returnobjectPool.get(name);
}
publicstaticvoidmain(String[]args){
ObjectPoolFactoryopf=newObjectPoolFactory();
opf.initProperties("objectProps.txt");
System.out.println(opf.getObject("b"));
}
}
3.2.3调用方法
调用某个Class对象的getMethods()之后就可以获取所有的方法,也就是Method对象,每个Method对象都包含一个invoke方法。
Objectinvoke(Objectobj,Object…args):
该方法中的obj是执行该方法的主调,后面的args是执行该方法时的实参。
示例代码:
publicvoidsayHello(Stringstr){
System.out.println("SayHello方法:
"+str);
}
publicstaticvoidmain(String[]args){
Class
try{
Objectobj=clz.newInstance();
Methodt=clz.getMethod("sayHello",String.class);
t.invoke(obj,"HelloWorld!
");
}catch(Exceptione){
e.printStackTrace();
}
}
3.2.4访问属性:
通过Class的getField()方法可以获取该类所包含的全部Field(属性)或指定Field。
Field提供了两组方法来访问属性:
ØgetXXX(Objectobj):
获取obj对象该Field的属性值,XXX代表基本类型,如为引用类型则去掉XXX。
ØsetXXX(Objectobj,XXXval):
将obj对象有该Field设置成val值。
XXX解释同上。
ps:
使用这两个方法可以访问任意属性,包括private属性
publicclassPerson{
privateStringname;
privateintage;
publicStringtoString(){
return"姓名:
"+name+"\t年龄:
"+age;
}
publicstaticvoidmain(String[]args){
Personp=newPerson();
Class
FieldnameField;
try{
nameField=clzP.getDeclaredField("name");//获取name属性
nameField.setAccessible(false);//忽略可访问性
nameField.set(p,"zhangsan");//设置p对象的该属性值
FieldageField=clzP.getDeclaredField("age");
ageField.setAccessible(false);
ageField.set(p,22);
}catch(Exceptione){
e.printStackTrace();
}
System.out.println(p);
}
}
三、动态代理和AOP
AOP:
面向切面编程。
程序示例:
接口:
Dog;类:
GunDog;切面类:
DogUtil;代理类:
MyInvokactionHandler;工厂类:
MyProxyFactory
接口:
publicinterfaceDog{
publicvoidinfo();
publicvoidrun();
}
实现类:
publicclassGunDogimplementsDog{
publicvoidinfo(){
System.out.println("这是一只猎狗。
。
");
}
publicvoidrun(){
System.out.println("在速度奔跑...");
}
}
切面类:
publicclassDogUtil{
publicvoidbeforeMethod(){
System.out.println("=====模拟第一个通用方法=====");
}
publicvoidafterMethod(){
System.out.println("=====模拟第二个通用方法=====");
}
}
代理类:
publicclassMyInvokationHandlerimplementsInvocationHandler{
privateObjecttarget;
publicvoidsetTarget(Objectobj){
this.target=obj;
}
publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)
throwsThrowable{
DogUtildu=newDogUtil();
du.beforeMethod();
//调用方法
Objectresult=method.invoke(target,args);
du.afterMethod();
returnresult;
}
}
代理工厂:
publicclassMyProxyFactory{
publicstaticObjectgetProxy(Objecttarget){
MyInvokationHandlerhandler=newMyInvokationHandler();
handler.setTarget(target);
//第一个参数是获取类加载器,第二个参数是目标类接口,第三个为代理对象
returnProxy.newProxyInstance(target.getClass().getClassLoader()
target.getClass().getInterfaces(),handler);
}
}
测试类:
publicstaticvoidmain(String[]args){
Dogtarget=newGunDog();
Dogdog=(Dog)MyProxyFactory.getProxy(target);
dog.info();
dog.run();
}
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第三 加载 反射