类的加载
理论知识及例子来自李刚的疯狂java讲义,在此感谢李老师!!!
类的加载由类加载器完成,类加载器通常由JVM提供,JVM提供的类加载器我们称之为系统类加载器。除此之外我们还可以通过几成ClassLoader基类来自定义类加载器
- 从本地文件加载class文件
- 从jar包中加载class文件,就像jdbc:oracle:OracleDriver似的
- 通过网络读取加载class文件
- 把一个java文件动态编译并加载
类被加载之后会在系统中为之生成一个Class对象,接着将会进入连接阶段
连接阶段会把类的二进制文件加载到jre中,连接阶段又可以分为以下三步:
- 验证:检验被加载的类是否有正确的内部结构,并和其他类协调一致
- 准备:为类的静态属性分配内存并初始化
- 解析:将类中二进制数据中的符号引用替换成直接引用
类的初始化阶段,虚拟机负责对类进行初始化,主要是对静态属性初始化,静态属性初始化有两种方式:(1)声明静态属性时直接初始化;(2)使用静态初始化块初始化;
publicclass StaticParameter { /** * 静态的final属性只能在两个位置初始化 * 1)声明时直接初始化 * 2)使用静态初始化块初始化 */ publicstaticfinalinta; static { a = 1; } } |
静态int属性不初始化则默认为0,String对象为null
publicclass StaticParameter { /** * 静态的final属性只能在两个位置初始化 * 1)声明时直接初始化 * 2)使用静态初始化块初始化 */ publicstaticfinalinta; publicstaticintb; publicstatic String str; static { a = 1; } publicstaticvoid main(String[] args) { System.out.println(b); //o System.out.println(str); //null } } |
JVM初始化一个类的步骤
- 假如这个类还没被加载和链接,那么程序先加载连接该类
- 假如该类的直接父类还没有初始化,则先初始化其直接父类
- 假如类中有初始化块,则按照前后顺序依次初始化
类初始化的时机
当java程序通过下面6种方式使用某个类或者某个接口时,系统会初始化该类或者接口。
- 创建类的实例(使用new关键字、使用类的反射、使用反序列化创建实例)
- 调用这个类的静态方法
- 访问它的静态属性或者为其静态属性初始化
- 使用反射方式强制创建某个类或某个接口对应的Class对象,如Class.forName(“Person”),如果系统还未初始化Person类则此操作会导致Person类被初始化
- 初始化该类的子类
- 直接使用java.exe来运行该类
需要注意的是:对于final类型的静态属性,如果在编译时就可以得到属性值(初始化),则可认为该属性可以被当成编译时常量,。当程序使用编译时常量,不会导致该类的初始化。
publicclass FinalStaticLoader { publicstaticvoid main(String[] args) { System.out.println(Test.str); //输出结果为adfggg,可以看出Test类并没有被加载 } } class Test{ static{ System.out.println("类被初始化加载"); } publicstaticfinal String str = "adfggg"; } |
如果一个类的静态final属性在运行时才能够确定属性值,则调用此属性就会初始化此类。
publicclass FinalStaticLoader { publicstaticvoid main(String[] args) { System.out.println(Test.time); //输出结果为1386331930159,可以看出Test类被加载了 } } class Test{ static{ System.out.println("类被初始化加载"); } publicstaticfinallongtime = System.currentTimeMillis(); } |
类加载器
JVM启动时会形成三个类加载器组成的初始类加载器层次结构
- Bootstrap ClassLoader: 根类加载器
- Extension ClassLoader: 扩展类加载器
- System ClassLoader: 系统类加载器
根类加载器负责java的核心类加载,并不是ClassLoader的子类,而是由JVM自身实现的。
扩展类加载器负责jre的扩展目录(jdk1.7.0_21\jre\lib\ext)中jar的类包,通过这个功能我们可以为java核心类扩展核心类以外的其他功能(将我们自己制作的jar包放在该目录下)。
系统类加载器负载JVM启动时加载来自命中java中的-classpath或者java.class.path属性的,或CLASSPATH环境变量所指定的jar包或类路径。如果没有特别指定则用户自定义的类加载器你都是用系统类加载器作为父类。
创建并使用自己的类加载器:
JVM中除了根加载器之外的所有类加载器都是ClassLoader的子类实例,ClassLoader包含了大量的protected方法——这些方法都可以被子类重写。
URLClassLoader类:
java为ClassLoader提供了一个URLClassLoader实现类,该类也是系统类加载器和扩展类加载器的父类(继承关系,而不是父类加载器),我们可以直接使用URLClassLoader来加载类,常用构造方法为:
URLClassLoader(URL[] urls):使用默认的父类加载器创建一个ClassLoader对象,该对象将从urls指定的系列路径查询并加载类。
URL[] urls = {new URL("file:mysql.jar")}; URLClassLoader myClassLoader = new URLClassLoader(urls); Driver driver = (Driver)myClassLoader.loadClass("com.mysql.jdbc.Driver").newInstance(); |
URL可以以file:http:ftp:作为前缀表示从文件。网络。ftp加载。
通过反射获取类的信息
java多态机制使得java程序许多对象在运行时出现两种类型,编译时类型和运行时类型。假如从外部传来一个对象供我们使用,对象的编译类型为Object我们如何获知它的运行时类型呢?
- 如果我们明确知道对象的编译、运行时类型则可以使用instanceof进行判断后强制类型转换
- 如果我们不知道对象的编译时类型,程序只能靠运行时获取它的信息,这时则必须使用反射
- 使用反射机制获取类信息步骤:获得Class对象
每个类被加载之后,系统会为孩子生成一个Class对象,通过该Class对象则可以访问JVM中的这个类。java中获取Class对象有如下三种方式
- 使用Class。forName(String str)方式获取,参数为类的全限定名(必须添加完整包名)
- 调用某个类的class属性,比如Person.class会返回Person类对应的Class对象
- 调用某个对象的getClass()方法,这个方法位于java.lang.Object下因此每个对象都可调用,该方法返回该对象所属类对应的Class对象
对于第一种和第二种方法都是直接使用类来获取该类对应的Class对象,第二种方法更为常用;因为他可以在类编译期间就判定该类的Class对象是否存在,且无需调用方法更加效率。
从Class对象中获取信息
通过Class对象可以获取大量的Method Constructor,Field等属性,这些对象分别代表该类所包括的方法,构造器,和属性,我们可以通过这些对象来执行实际的功能:例如调用方法、创建实例。
|
asSubclass(Class<U> clazz) |
|
boolean |
desiredAssertionStatus() |
|
static Class<?> |
||
static Class<?> |
forName(String name, boolean initialize, ClassLoader loader) |
|
|
getAnnotation(Class<A> annotationClass) |
|
getAnnotations() |
||
getCanonicalName() |
||
Class<?>[] |
getClasses() |
|
getClassLoader() |
||
Class<?> |
getComponentType() |
|
getConstructor(Class<?>... parameterTypes) |
||
Constructor<?>[] |
getConstructors() |
|
getDeclaredAnnotations() |
||
Class<?>[] |
getDeclaredClasses() |
|
getDeclaredConstructor(Class<?>... parameterTypes) |
||
Constructor<?>[] |
getDeclaredConstructors() |
|
getDeclaredField(String name) |
||
Field[] |
getDeclaredFields() |
|
getDeclaredMethod(String name, Class<?>... parameterTypes) |
||
Method[] |
getDeclaredMethods() |
|
Class<?> |
getDeclaringClass() |
|
Class<?> |
getEnclosingClass() |
|
Constructor<?> |
getEnclosingConstructor() |
|
getEnclosingMethod() |
||
T[] |
getEnumConstants() |
|
getField(String name) |
||
Field[] |
getFields() |
|
Type[] |
getGenericInterfaces() |
|
getGenericSuperclass() |
||
Class<?>[] |
getInterfaces() |
|
getMethod(String name, Class<?>... parameterTypes) |
||
Method[] |
getMethods() |
|
int |
getModifiers() |
|
getName() |
||
getPackage() |
||
getProtectionDomain() |
||
getResource(String name) |
||
getResourceAsStream(String name) |
||
Object[] |
getSigners() |
|
getSimpleName() |
||
getSuperclass() |
||
TypeVariable<Class<T>>[] |
getTypeParameters() |
|
boolean |
isAnnotation() |
|
boolean |
isAnnotationPresent(Class<? extends Annotation> annotationClass) |
|
boolean |
isAnonymousClass() |
|
boolean |
isArray() |
|
boolean |
isAssignableFrom(Class<?> cls) |
|
boolean |
isEnum() |
|
boolean |
isInstance(Object obj) |
|
boolean |
isInterface() |
|
boolean |
isLocalClass() |
|
boolean |
isMemberClass() |
|
boolean |
isPrimitive() |
|
boolean |
isSynthetic() |
|
newInstance() |
||
toString() |
测试
publicclass Person { public inti = 1; //私有构造器 private Person(){} //公有带参数的构造器 public Person(String name){ System.out.println("有参数的公有构造器"); }
//无参普通方法 publicvoid info(){ System.out.println("无参普通方法"); } //有参的普通方法 publicvoid info(String str){ System.out.println("有参的普通方法"); }
//内部类 class Inner{}
//通过反射机制获取类的信息 publicstaticvoid main(String[] args) throws Exception{
//获取Person类的Class对象 Class<Person> c = Person.class;
System.out.println("该Class对象对应的包名为:"+c.getPackage()); System.out.println("父类为:"+c.getSuperclass());
//获取该Class对象对应的全部构造器 Constructor<?>[] ctor = c.getDeclaredConstructors(); System.out.println("打印Person类的全部构造器"); for(Constructor cs:ctor){ System.out.println(cs); }
//获取该Class对象对应的全部public构造方法 Constructor[] pubCtor = c.getConstructors(); System.out.println("Person类的public构造方法"); for(Constructor pubc:pubCtor){ System.out.println(pubc); }
//获取Class对象对应的全部public方法,父类对应的方法也会被取出 Method[] m = c.getMethods(); System.out.println("全部public方法"); for(Method m1:m){ System.out.println(m1); }
//获取内部类 Class[] inner = c.getDeclaredClasses(); System.out.println("打印内部类"); for(Class cIn:inner){ System.out.println(cIn); } } } |
使用反射生成并操作对象
Clss对象可以获取该类里包含的方法(Method),属性(Field)和构造器(Constructor),程序可以使用Method对象来执行对应的方法,通过Constructor对象调用对应的构造器你创造对象。
<!--[if !supportLists]-->1、 <!--[endif]-->创建对象
使用Class的newInstance()方法创建一个该Class对应类的一个实例,此方法需要调用该类的默认构造器,因此对应得类需要有默认构造器才可以
使用Clss对象获取对应的Constructor对象,再调用Constructor对象的newInstance()方法创建实例,此种方法可以使用指定构造器创建实例。
通过第一种方式比较常见,java很多框架中都使用配置文件创建java对象,从配置文件读取的时字符串类名,需要通过该字符串创建对应的类就需要使用反射的机制。
测试程序:创建一个对象池,对象池根据name-value对创建对象
publicclass ObjectPoolFactory { //定义一个对象池,前面是对象名,后面是实际对象 private Map<String,Object> objectPool = new HashMap<String,Object>(); //定义一个创建对象的方法,该方法只要传入一个字符串类名,就可以根据该类名创建一个对象 private Object createObject(String className) throws ClassNotFoundException, InstantiationException, IllegalAccessException{ //根据字符串获取对应的Cladd对象 Class<?> c = Class.forName(className); //使用默认构造器创建实例 return c.newInstance(); }
//读取properties配置文件创建实例并初始化对象池 publicvoid initPoll(String fileName) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException{ FileInputStream fis = null; fis = new FileInputStream(fileName); Properties pro = new Properties(); pro.load(fis); for(String name:pro.stringPropertyNames()){ //每取出一个键值对则根据属性值创建一个对象,调用createObjecy()方法生成对象放入对象池 objectPool.put(name, createObject(pro.getProperty(name))); } fis.close(); }
//从对象池中获取指定name的对象 public Object getObject(String name){ returnobjectPool.get(name); }
publicstaticvoid main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException { ObjectPoolFactory opf = new ObjectPoolFactory(); opf.initPoll("obj.properties"); System.out.println(opf.getObject("a")); } } |
如果不想使用默认构造器创造实例,则需要使用Constructor对象对应的构造器
- 获取该类的Class对象
- 利用Class的getConstructor()方法获取指定构造器
- 调用特定的Constructor的newInstance()方法创建对象
对已知类的创建一般不适用反射机制,毕竟效率要低一些。但是当程序需要动态创建某个类的对象时会考虑使用反射,SPRING框架使用较多的反射机制。
调用方法
当获取某个类对应的Class对象之后则可以使用getMethods()或者getMethod()方法获取全部或者指定的方法;每个Method对象对应一个方法,获取Method之后就可以使用Method的invoke方法调用指定方法
Object invok(Object obj,Object para):obj为主调,后面的为参数
下面对对象池的功能进行加强,,程序允许在配置文件中增加对象的属性值,对象池工厂会读取对象的属性值并调用对象的setter方法为对象初始化属性。
publicclass ExtendedObjectPollFactory { //创建一个对象池,key为对象名,value为对象类型 private Map<String,Object> objPool = new HashMap<String,Object>(); //读取配置文件初始化Properties对象 private Properties pro = new Properties(); publicvoid initPro(String fileName){ FileInputStream fis = null; try { fis = new FileInputStream(fileName); pro.load(fis); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ if(fis != null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } }
//定义创建对象的方法,传入一个字符串类名则创建一个对象 public Object createObj(String name) throws ClassNotFoundException, InstantiationException, IllegalAccessException{ //使用字符串类名获取该类对应的Class对象 Class c = Class.forName(name); //使用newInstance方法获取一个实例 return c.newInstance(); }
//根据配置文件初始化对象池 publicvoid initPoll() throws ClassNotFoundException, InstantiationException, IllegalAccessException{ //初始化对象池 for(String name:pro.stringPropertyNames()){ //取出一对键值对(key-value),如果键值对中不包含%则根据value创建一个实例(value为一个类名)并放入对象池 if(!name.contains("%")){ objPool.put(name, createObj(pro.getProperty(name))); } } } //为对象池中的对象属性初始化 publicvoid initProperty() throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException{ for(String name:pro.stringPropertyNames()){ //如果名字中包含%则认为是设置的属性 if(name.contains("%")){ String[] objAndPro = name.split("%"); //取出需要设置属性的目标对象 Object target = getObject(objAndPro[0]); //该属性对应的setter方法为set+”属性首字母大写“+属性其余字母小写 String mtdName = "set"+objAndPro[1].substring(0,1).toUpperCase()+objAndPro[1].substring(1); //通过target对象的getClass方法获取他对应的Class对象 Class c = target.getClass(); //获取该属性对应的setter方法 Method mtd = c.getMethod(mtdName, String.class); //通过Method的invoke方法执行setter方法 mtd.invoke(target, pro.getProperty(name)); } } } //从对象池中根绝对象名取出对象的方法 public Object getObject(String name){ returnobjPool.get(name); }
publicstaticvoid main(String[] args) { ExtendedObjectPollFactory opf = new ExtendedObjectPollFactory(); opf.initPro("cong.properties"); try { opf.initPoll(); opf.initProperty(); System.out.println(opf.getObject("a")); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } |
配置文件
a=javax.swing.JFrame b=javax.swing.JLabel a%title=TestTitle |
a%itle表示a的title属性,其值为Test Title;spring框架就是将对象以及属性值都放置在配置文件中进行管理使得系统得到良好的解耦
此外如果需要访问类中的private属性或者private方法则需要有调用此类方法的权限,如果程序需要调用其私有方法或者属性可以使用Method的setAccessible(boolean flag)方法预先取消java的语言访问权限检查(setAccessible()方法时Method父类的方法)
访问属性值
通过Class的getField(或者getFields()方法可以获得Class的全部属性或者指定属性
getXxx(Object obj):获取指定对象的属性值,此处的Xxx对应8中基本类型,如果该属性时引用类型则取消Xxx;
setXxx(Object obj,Xxx val):将obj对象的该Field属性值设置为val,Xxx对应8中基本类型,如果为引用类型则取消Xxx。
使用这两个方法可以随意访问指定对象的所有属性,包括私有属性
publicclass FieldTest { publicstaticvoid main(String[] args) { Student s = new Student(); //获取Student类对应的的Class对象 Class sClass = s.getClass(); try { /** * getField()方法只能获取public属性 * getDeclaredField()方法则可以获得所有属性 */ //获取Student中名称为name的属性 Field nameField = sClass.getDeclaredField("name"); //取消访问权限检查 nameField.setAccessible(true); //调用set方法为nameField属性设置值 nameField.set(s, "pursuit"); //获取名为age的属性 Field ageField = sClass.getDeclaredField("age"); //取消访问权限检查 ageField.setAccessible(true); ageField.set(s, 21); System.out.println(s); //输出结果为Student [name=pursuit, age=21] } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } class Student{ private String name; privateintage; public String toString() { return"Student [name=" + name + ", age=" + age + "]"; } } |
操作数组:
java.lang.reflact报下提供了一个Array类,该类可以代表所有类型的数组。
程序可以使用Array类来动态创建数组并操作数组
static Object newInstance(Class,length):创建一个指定元素类型指定维度与长度的数组
static void setXXX(Object arry,index val):将数组中指定索引处的元素值设置为val,如果次数组元素是引用类型则使用set(Objec obt,index Object val)
publicclass ArrayTest { publicstaticvoid main(String[] args) { //创建一个长度为10的元素类型为String的数组 Object ary = Array.newInstance(String.class, 10); Array.set(ary, 0, "反射数组"); Array.set(ary,1,"sfafa"); //取出元素的值 Object ary1 = Array.get(ary, 0); Object ary2 = Array.get(ary, 1); System.out.println(ary1); //反射数组 System.out.println(ary2); //sfafa } } |
相关推荐
Java反射是Java编程语言中的一个重要特性,它允许程序在运行时动态地获取类的信息并操作类的对象。在Java中,反射机制提供了强大的能力,包括在运行时检查类的结构、创建对象实例、调用方法以及访问和修改字段值。...
Java反射是Java编程语言中的一个强大工具,它允许运行中的Java程序对自身进行检查并且可以直接操作程序的内部属性。在Java中,反射主要用于在运行时分析类和对象,包括访问私有成员、调用私有方法、创建对象、获取类...
Java反射机制是Java编程语言中的一个强大特性,它允许运行中的Java程序对自身进行检查并且可以直接操作程序的内部属性。这个特性使得Java具有了高度的灵活性和动态性,尤其是在处理元数据、创建对象、调用私有方法...
Java反射机制是Java语言提供的一种强大功能,它允许运行中的Java程序对自身进行检查并且可以直接操作程序的内部属性。在Java中,反射机制的核心类是java.lang.Class,它代表了运行时的类信息。通过Class对象,我们...
在本文中,我们将深入探讨如何使用Java反射来获取并执行某个特定的方法。 首先,我们需要了解Java反射的基本概念。`java.lang.Class`类是反射的核心,它代表了Java中的每一个类。我们可以通过以下方式获取到一个...
### Java反射性能测试分析 #### 引言 Java反射机制是Java编程语言中一个强大的特性,它允许程序在运行时动态地访问、检测和修改类、接口、字段和方法等对象。然而,反射操作通常会引入额外的开销,这在性能敏感的...
### Java反射机制总结 #### 反射的概念与起源 反射的概念最早由Smith于1982年提出,指的是程序能够访问、检测并修改其自身状态或行为的能力。这一概念的提出迅速引起了计算机科学领域的广泛关注,并在之后的研究中...
Java反射是Java编程语言中的一个强大特性,它允许运行时的程序访问并操作类、接口、字段和方法等信息,即使这些信息在编译时并未明确知晓。在Java中,反射通常通过`java.lang.Class`类和相关的API来实现。本实例将...
### Java反射机制详解 #### 一、概述 Java反射机制是一种强大的编程技术,它允许运行时检查类的信息并操作对象的内部结构。本篇将基于Sun公司的官方文档《Java™ Core Reflection API and Specification》(1997年...
### 反射实例—JAVA反射机制 #### 一、反射概念及原理 反射在计算机科学领域,特别是程序设计中,是指程序有能力访问、检测和修改其自身的结构和行为。这一概念最早由Smith于1982年提出,并迅速应用于各种编程语言...
### Java反射机制详解 #### 一、什么是Java反射? Java反射是Java编程语言的一个特性,它允许运行时检查和操作程序结构(类、字段、方法等)。反射的主要用途包括但不限于:动态实例化对象、访问私有成员、调用...
Java反射是Java语言提供的一种强大的动态类型特性,它允许程序在运行时检查类、接口、字段和方法的信息,并且能够动态地创建对象和调用方法。这个能力使得开发者可以突破静态类型的束缚,实现一些在编译时期无法完成...
Java反射是Java编程语言中的一个强大特性,它允许在运行时检查类、接口、字段和方法的信息,并且能够在运行时动态地创建对象和调用方法。这个特性使得Java具有高度的灵活性,尤其在处理框架、插件系统以及元数据驱动...
### Java反射机制详解 #### 一、反射的基本概念与历史背景 反射的概念最早由Smith在1982年提出,其核心思想是程序有能力访问、检测甚至修改自身的状态和行为。这种能力一经提出,迅速成为了计算机科学领域的研究...
### Java反射机制与NoSuchMethodException详解 在Java编程中,反射是一种强大的机制,允许程序在运行时检查和修改自身结构和行为。然而,当开发者尝试使用反射调用一个不存在的方法时,便会遇到`java.lang....
Java反射是Java编程语言中的一个强大工具,它允许运行中的Java程序对自身进行检查并且可以直接操作程序的内部属性。在Java中,反射主要用于在运行时分析类和对象,包括访问私有成员、调用私有方法、创建动态代理等。...
Java反射是Java编程语言中的一个强大工具,它允许运行中的Java程序对自身进行检查并且可以直接操作程序的内部属性。在给定的“利用java反射将json字符串转成对象”的主题中,我们将深入探讨如何借助反射机制将JSON...
### Java反射机制详解 #### 一、什么是Java反射机制? Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的...
Java反射机制是Java编程语言中的一个强大工具,它允许程序在运行时检查并操作类、接口、字段和方法等对象。在"北大青鸟java反射机制"的学习资料中,我们将会深入探讨这一核心特性。 首先,我们要理解反射的核心概念...