3反射机制.docx
- 文档编号:16905031
- 上传时间:2023-07-19
- 格式:DOCX
- 页数:50
- 大小:279.88KB
3反射机制.docx
《3反射机制.docx》由会员分享,可在线阅读,更多相关《3反射机制.docx(50页珍藏版)》请在冰点文库上搜索。
3反射机制
《软件设计模式》讲义
3、反射与工厂模式
反射、工厂模式应用
教学重点
1.软件设计原则中开闭原则、单一职责原则
2.简单工厂设计模式
3.Java中的反射技术
4.工厂设计模式
5.抽象工厂设计模式
教学难点
1.软件设计原则的理解
2.设计模式的应用场景,设计模式使用后带来的优点,不用设计模式的缺点
3.反射的作用,反射的用法
教学目标
1.理解开闭原则
2.理解单一职责
3.掌握反射技术
4.了解简单工厂设计模式、工厂设计模式及抽象工厂设计模式之间的区别
5.理解创建型设计模式解决的问题类型
6.完成老师的课堂实例
1、JAVA运行原理
●教学时间:
5分钟
●教学方法:
PPT,回顾
这一节课我们准备对JAVA的反射机制进行研究,在此之前,先和大家讨论一下,什么是JAVA,你们了解JAVA到什么程度?
对于JAVA的底层知识有没有一些觉得自己可以炫耀的。
(讨论,举手)
下面我来问几个我想知道的问题,比较基础啊,看看有没有谁能够帮我解释的。
对象创建的流程分析,看PPT,编写代码,请学生回答问题
2、类的加载过程分析
●教学时间:
5分钟
●教学方法:
PPT,回顾
1)类的加载过程
JVM将类加载过程分为三个步骤:
装载(Load),链接(Link)和初始化(Initialize)链接又分为三个步骤,如下图所示:
(1)装载:
查找并加载类的二进制数据;
类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个这个类的java.lang.Class对象,用来封装类在方法区类的对象。
JVM的类加载是通过ClassLoader及其子类来完成的,类的层次关系和加载顺序可以由下图来描述:
●BootstrapClassLoader
负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类
●ExtensionClassLoader
负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包
●AppClassLoader
负责记载classpath中指定的jar包及目录中class
●CustomClassLoader
属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader
加载过程中会先检查类是否被已加载,检查顺序是自底向上,从CustomClassLoader到BootStrapClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。
而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。
类加载器的特点:
1、运行一个程序时,总是由AppClassLoader(系统类加载器)开始加载指定的类。
2、在加载类时,每个类加载器会将加载任务上交给其父,如果其父找不到,再由自己去加载。
3、BootstrapLoader(启动类加载器)是最顶级的类加载器了,其父加载器为null.
类加载有三种方式:
1、命令行启动应用时候由JVM初始化加载
2、通过Class.forName()方法动态加载
3、通过ClassLoader.loadClass()方法动态加载
三种方式区别比较大,看个例子就明白了:
publicclassHelloWorld{
publicstaticvoidmain(String[]args)throwsClassNotFoundException{
ClassLoaderloader=HelloWorld.class.getClassLoader();
System.out.println(loader);
//使用ClassLoader.loadClass()来加载类,不会执行初始化块
loader.loadClass("Test2");
//使用Class.forName()来加载类,默认会执行初始化块
//Class.forName("Test2");
//使用Class.forName()来加载类,并指定ClassLoader,初始化时不执行静态块
//Class.forName("Test2",false,loader);
}
}
publicclassTest2{
static{
System.out.println("静态初始化块执行了!
");
}
}
分别切换加载方式,会有不同的输出结果。
类加载器的获取分析:
publicclassHelloWorld{
publicstaticvoidmain(String[]args){
HelloWorldhello=newHelloWorld();
Classc=hello.getClass();
ClassLoaderloader=c.getClassLoader();
System.out.println(loader);
System.out.println(loader.getParent());
System.out.println(loader.getParent().getParent());
}
}
打印结果:
sun.misc.Launcher$AppClassLoader@19821f
sun.misc.Launcher$ExtClassLoader@addbf1
null
从上面的结果可以看出,并没有获取到ExtClassLoader的父Loader,原因是BootstrapLoader(启动类加载器)是用C语言实现的,找不到一个确定的返回父Loader的方式,于是就返回null。
(2)链接:
●验证:
确保被加载类的正确性;
●准备:
为类的静态变量分配内存,并将其初始化为默认值;
●解析:
把类中的符号引用转换为直接引用;
(3)初始化:
为类的静态变量赋予正确的初始值;
准备阶段和初始化阶段看似有点矛盾,其实是不矛盾的,如果类中有语句:
privatestaticinta=10,它的执行过程是这样的,首先字节码文件被加载到内存后,先进行链接的验证这一步骤,验证通过后准备阶段,给a分配内存,因为变量a是static的,所以此时a等于int类型的默认初始值0,即a=0,然后到解析(后面在说),到初始化这一步骤时,才把a的真正的值10赋给a,此时a=10。
2)类的初始化
类什么时候才被初始化:
(1)创建类的实例,也就是new一个对象
(2)访问某个类或接口的静态变量,或者对该静态变量赋值
(3)调用类的静态方法
(4)反射(Class.forName("com.lyj.load"))
(5)初始化一个类的子类(会首先初始化子类的父类)
(6)JVM启动时标明的启动类,即文件名和类名相同的那个类
只有这6中情况才会导致类的类的初始化。
类的初始化步骤:
(1)如果这个类还没有被加载和链接,那先进行加载和链接
(2)假如这个类存在直接父类,并且这个类还没有被初始化(注意:
在一个类加载器中,类只能初始化一次),那就初始化直接的父类(不适用于接口)
(3)加入类中存在初始化语句(如static变量和static块),那就依次执行这些初始化语句。
3、类的加载过程实例分析
●教学时间:
5分钟
●教学方法:
PPT,回顾
Java程序运行的场所是内存,当在命令行下执行:
javaHelloWorld
命令的时候,JVM会将HelloWorld.class加载到内存中,并形成一个Class的对象HelloWorld.class。
其中的过程就是类加载过程:
1、寻找jre目录,寻找jvm.dll,并初始化JVM;
2、产生一个BootstrapLoader(启动类加载器);
3、BootstrapLoader自动加载ExtendedLoader(标准扩展类加载器),并将其父Loader设为BootstrapLoader。
4、BootstrapLoader自动加载AppClassLoader(系统类加载器),并将其父Loader设为ExtendedLoader。
5、最后由AppClassLoader加载HelloWorld类。
以上就是类加载的最一般的过程。
1、BootstrapLoader(启动类加载器):
加载System.getProperty("sun.boot.class.path")所指定的路径或jar。
2、ExtendedLoader(标准扩展类加载器ExtClassLoader):
加载System.getProperty("java.ext.dirs")所指定的路径或jar。
在使用Java运行程序时,也可以指定其搜索路径,例如:
java-Djava.ext.dirs=d:
\projects\testproj\classesHelloWorld
3、AppClassLoader(系统类加载器AppClassLoader):
加载System.getProperty("java.class.path")所指定的路径或jar。
在使用Java运行程序时,也可以加上-cp来覆盖原有的Classpath设置,例如:
java-cp./lavasoft/classesHelloWorld
类的加载和初始化是两个过程
classTester{
static{
System.out.println("Tester类的静态初始化块儿......");
}
}
publicclassClassLoaderTest{
publicstaticvoidmain(String[]args)throwsClassNotFoundException{
ClassLoadercl=ClassLoader.getSystemClassLoader();
cl.loadClass("pany.Tester");
System.out.println("系统加载Tester类完毕");
Class.forName("pany.Tester");
}
}
4、反射的概念
●教学时间:
5分钟
●教学方法:
PPT,回顾
想要灵活使用一个类,我们必须掌握上面给大家说的这些类加载器的工作原理,把一个类加载和初始化后,再进行实例化和使用。
那么一个类在内存中又是如何存储的呢?
反射就是把Java类中的各种成分映射成相应的Java类,例如一个Java类中用一个Class类的对象表示,一个类中的组成部分:
成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就想七成是一个类,汽车中的发动机,变速箱等等也是一个个的类。
表示Java的Class类显然要提供一系列的方法,来获得其中的变量、方法、构造方法,修饰符、包等信息,这些信息就是用相应的类的实例对象来表示,他们是Field、Method、Contructor、Package等等。
5、认识Class类
●教学时间:
5分钟
●教学方法:
PPT,回顾
要使用反射,我们需要了解和反射相关的几个类:
Class、Method、Field等等
首先我们来看看我们经常使用的熟悉的一个反射相关的类Class,对这个类大家都不陌生了,在我们学习java高级的时候,讲mysql连接,老师都有给大家说,需要先加载驱动,实际上就是需要讲驱动类加载到内存中,供JDBC使用。
当时大家使用的操作应该是:
Class.forName(“类的全称”);
Com.mysql.jdbc.driver、org.gjt.mm.mysql.Driver
但是,老师并没有深入去讲解这个类的使用,以及它与反射的关系,不过这个其实你们应该自己课下自己就去学习了,下面我们就来一起看看这个类是怎么回事。
Class没有公共构造方法。
Class对象是在加载类时由Java虚拟机以及通过调用类加载器中的defineClass方法自动构造的,因此不能显式地声明一个Class对象。
下面我们来看一下Class对象的获取方式,看下面这个表格:
获取方式
说明
示例
object.getClass()
每个对象都有此方法
获取指定实例对象的Class
Listlist=newArrayList();
ClasslistClass=list.getClass();
class.getSuperclass()
获取当前Class的继承类Class
Listlist=newArrayList();
ClasslistClass=list.getClass();
ClasssuperClass=listClass.getSuperclass();
Object.class
.class直接获取
ClasslistClass=ArrayList.class;
Class.forName(类名)
用Class的静态方法,传入类的全称即可
try{
Classc=Class.forName("java.util.ArrayList");
}catch(ClassNotFoundExceptione){
e.printStackTrace();
}
Primitive.TYPE
基本数据类型的封装类获取Class的方式
ClasslongClass=Long.TYPE;
ClassintegerClass=Integer.TYPE;
ClassvoidClass=Void.TYPE;
publicclassClassInfo{
publicstaticvoidmain(String[]args){
Classclz=null;
try{
clz=Class.forName(args[0]);
System.out.println("类全程:
"+clz.getName());
System.out.println("是否接口:
"+clz.isInterface());
System.out.println("是否基本类型:
"+clz.isPrimitive());
System.out.println("是否为数组对象:
"+clz.isArray());
System.out.println("父类的名字是:
"+clz.getSuperclass().getName());
}catch(ClassNotFoundExceptione){
e.printStackTrace();
}catch(ArrayIndexOutOfBoundsExceptione1){
System.out.println("没有输入参数,指定的类名");
}
}
}
6、通过Class对象获得,类的相关信息
●教学时间:
5分钟
●教学方法:
PPT,回顾
1、通过一个对象获得完整的包名和类名
Java代码
1package Reflect;
2
3/**
4 * 通过一个对象获得完整的包名和类名
5 * */
6class Demo{
7 //other codes...
8}
9
10class hello{
11 public static void main(String[] args) {
12 Demo demo=new Demo();
13 System.out.println(demo.getClass().getName());
14 }
15}
【运行结果】:
Reflect.Demo
添加一句:
所有类的对象其实都是Class的实例。
2、实例化Class类对象
Java代码
1package Reflect;
2class Demo{
3 //other codes...
4}
5
6class hello{
7 public static void main(String[] args) {
8 Class
> demo1=null;
9 Class
> demo2=null;
10 Class
> demo3=null;
11 try{
12 //一般尽量采用这种形式
13 demo1=Class.forName("Reflect.Demo");
14 }catch(Exception e){
15 e.printStackTrace();
16 }
17 demo2=new Demo().getClass();
18 demo3=Demo.class;
19
20 System.out.println("类名称 "+demo1.getName());
21 System.out.println("类名称 "+demo2.getName());
22 System.out.println("类名称 "+demo3.getName());
23
24 }
25}
【运行结果】:
类名称 Reflect.Demo
类名称 Reflect.Demo
类名称 Reflect.Demo
3、通过Class实例化其他类的对象
通过无参构造实例化对象
Java代码
1package Reflect;
2
3class Person{
4
5 public String getName() {
6 return name;
7 }
8 public void setName(String name) {
9 this.name = name;
10 }
11 public int getAge() {
12 return age;
13 }
14 public void setAge(int age) {
15 this.age = age;
16 }
17 @Override
18 public String toString(){
19 return "["+this.name+" "+this.age+"]";
20 }
21 private String name;
22 private int age;
23}
24
25class hello{
26 public static void main(String[] args) {
27 Class
> demo=null;
28 try{
29 demo=Class.forName("Reflect.Person");
30 }catch (Exception e) {
31 e.printStackTrace();
32 }
33 Person per=null;
34 try {
35 per=(Person)demo.newInstance();
36 } catch (InstantiationException e) {
37 // TODO Auto-generated catch block
38 e.printStackTrace();
39 } catch (IllegalAccessException e) {
40 // TODO Auto-generated catch block
41 e.printStackTrace();
42 }
43 per.setName("Rollen");
44 per.setAge(20);
45 System.out.println(per);
46 }
47}
【运行结果】:
[Rollen 20]
但是注意一下,当我们把Person中的默认的无参构造函数取消的时候,比如自己定义只定义一个有参数的构造函数之后,会出现错误:
比如我定义了一个构造函数:
Java代码
1public Person(String name, int age) {
2 this.age=age;
3 this.name=name;
4 }
然后继续运行上面的程序,会出现:
Java代码
1java.lang.InstantiationException:
Reflect.Person
2 at java.lang.Class.newInstance0(Class.java:
340)
3 at java.lang.Class.newInstance(Class.java:
308)
4 at Reflect.hello.main(hello.java:
39)
5Exception in thread "main" java.lang.NullPointerException
6 at Reflect.hello.main(hello.java:
47)
所以大家以后再编写使用Class实例化其他类的对象的时候,一定要自己定义无参的构造函数
4、通过Class调用其他类中的构造函数(也可以通过这种方式通过Class创建其他类的对象)
Java代码
1package
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 反射 机制