------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、类加载器
1、类的加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
1.1、 加载
就是指将class文件读入内存,并为之创建一个Class对象。任何类被使用时系统都会建立一个Class对象。
1.2 、连接:接阶段又可以分为三个子步骤:验证、准备和解析。
验证: 是否有正确的内部结构,确保java类型数据格式的正确性,并适于JVM使。
准备: 负责为类的静态成员分配内存,并设置默认初始化值
解析: 解析过程就是在类型的常量池中寻找类、接口、字段和方法的符号引用,把这些符号引用替换成直接引 用。这个阶段可以被推迟到初始化之后,当程序运行的过程中真正使用某个符号引用的时候 再去解析它。
1.3、类初始化时机
1)创建类的实例
2)访问类的静态变量,或者为静态变量赋值
3)调用类的静态方法
4)使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
5)初始化某个类的子类
6)直接使用java.exe命令来运行某个主类
2、类加载器
2.1、含义:负责将.class文件加载到内在中,并为之生成对应的Class对象。
2.2、类加载器的组成及作用
Bootstrap ClassLoader 根类加载器:也被称为引导类加载器,负责Java核心类的加载。比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
Extension ClassLoader 扩展类加载器:负责JRE的扩展目录中jar包的加载。在JDK中JRE的lib目录下ext目录
Sysetm ClassLoader 系统类加载器:负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。
二、反射
1、概述
1.1、反射机制:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
1.2、反射技术:其实就是动态加载一个指定的类,并获取该类中的所有的内容。而且将字节码文件封装成对象,并将字节码文件中的内容都封装成对象,这样便于操作这些成员。简单说:反射技术可以对一个类进行解剖。
反射的好处:大大的增强了程序的扩展性。
2、反射的基本步骤:
2.1获得Class对象,就是获取到指定的名称的字节码文件对象。
2.2实例化对象,获得类的属性、方法或构造函数。
2.3访问属性、调用方法、调用构造函数创建对象。
3、获取Class对象的三种方式:
3.1每一个类对象都有一个静态的属性class。弊端:必须要先明确该类。如果是明确地获得某个类的Class对象可以用此方式,主要用于传参。
Class clazz = Person.class;
3.2通过每个对象都具备的方法getClass来获取。弊端:必须要创建该类对象,才可以调用getClass方法。如果拿到了对象,不知道是什么类型可以用此方式,用于获得对象的类型。
Object obj = new Person();
Class clazz1 = obj.getClass();
3.3使用的Class类中的方法,静态的forName方法。指定什么类名(完整的类名),就获取什么类字节码文件对象,这种方式的扩展性最强,只要将类名的字符串传入即可,用于类加载。
String classname = “cn.itcast.reflect.Person”;
Class clazz = Class.forName(classname);//当类的字节码已经加载进了内存,就将该字节码返回;如果jvm还没有该字节码,就用类加载器加载,再返回加载的字节码。
前两种方式不利于程序的扩展,因为都需要在程序使用具体的类来完成。
三种方式得到的字节码都是同一个字节码:
String str1 = “abc”;
Class cls1 = str1.getClass();
Class cls2 = String.class;
Class cls3 = Class.forName(“java.lang.String”);
System.out.println(cls1 == cls2);//true
System.out.println(cls1 == cls3);//true
4、九个预定义的Class:
1)包括八种基本类型(byte、short、int、long、float、double、char、boolean)的字节码对象和一种返回值为void类型的void.class。
2)Integer.TYPE是Integer类的一个常量,它代表此包装类型包装的基本类型的字节码,所以和int.class是相等的。基本数据类型的字节码都可以用与之对应的包装类中的TYPE常量表示
3)只要是在源程序中出现的类型都有各自的Class实例对象,如int[].class。数组类型的Class实例对象,可以用Class.isArray()方法判断是否为数组类型的。
5、反射的使用:
5.1、Class类
1)类中的方法,不包括获取成员属性及方法对象
static Class forName(String className):返回与给定字符串名的类或接口的相关联的Class对象。
Class getClass():返回的是Object运行时的类,即返回Class对象即字节码对象
String getName():以String形式返回此Class对象所表示的实体名称。
String getSuperclass():返回此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class
boolean isArray():判定此Class对象是否表示一个数组
boolean isPrimitive():判断指定的Class对象是否是一个基本类型。
2)通过Class对象获取类实例
如:
String className="包名.Person";
Class clazz=Class.forName(className);
Object obj=clazz.newInstance();
5.2、反射获取成员方法并使用:
获取所有方法:
Method[] getMehtods():返回一个包含某些Method对象的数组,是所代表的的类中的公共成员方法。
Method[] getDeclareMethods():返回Method 对象的一个数组,这些对象反映此Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
获取单个方法:
Method getMethod(String name,Class<?>… parameterTypes):返回一个Method对象,它表示的是此Class对象所代表的类的指定公共成员方法。
Method getDeclaredMethod(String name,Class<?>… parameterTypes):返回一个Method 对象,该对象反映此Class 对象所表示的类或接口的指定已声明方法。name 参数是一个String,它指定所需方法的简称,parameterTypes 参数是Class 对象的一个数组,它按声明顺序标识该方法的形参类型。
使用方法:Method类方法。
void setAccessible(boolean)方法:值为true时取消 Java 语言访问检查
Object invoke(Object obj ,参数);//调用对象的method方法
程序示例:
[java] view plaincopy package itcast; import java.lang.reflect.Constructor; import java.lang.reflect.Method; /** * @author oyfc */ public class MethodDemo { /** * 通过反射获取成员方法并使用。 * @param args * @throws ClassNotFoundException */ public static void main(String[] args) throws Exception { //获取字节码文件对象 String className = "itcast.Person"; Class class1 = Class.forName(className); //创建对象 Constructor constructor = class1.getConstructor(String.class,int.class); Object object = constructor.newInstance("zhangfei",21); //获取所有成员方法 Method[] methods = class1.getDeclaredMethods(); for(Method meth: methods){ sop(meth); } //获取成员方法 Method method = class1.getDeclaredMethod("show"); //暴力访问 method.setAccessible(true); //调用方法 method.invoke(object, null); } /** * @param method */ private static void sop(Object object) { System.out.println(object); } }
5.3反射类的构造函数并使用:
获取所有构造方法:
Constructor<?>[] getConstructors()返回一个包含某些Constructor 对象的数组,这些对象反映此Class 对象所表示的类的所有公共构造方法。
Constructor<?>[] getDeclaredConstructors()返回Constructor 对象的一个数组,这些对象反映此Class 对象表示的类声明的所有构造方法。它们是公共、保护、默认(包)访问和私有构造方法。
获取单个构造方法:
Constructor<T> getConstructor(Class<?>...parameterTypes):返回一个Constructor 对象,它反映此Class 对象所表示的类的指定公共构造方法。
Constructor<T> gettDeclaredConstructor():
使用方法: Constructor类的方法
T newInstance(Object ... initargs)):创建此Class对象所表示的类的一个新实例。
</pre>程序示例:</p><p><span style="font-size:18px;"></span><pre name="code" class="java">[java] view plaincopy
package itcast;
import java.lang.reflect.Constructor;
/**
* 通过反射获取构造方法并使用。
*
* @author oyfc
*/
public class ConstructorDemo {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// 获取字节码文件对象
String className = "itcast.Person";
Class class1 = Class.forName(className);
// 获取所有所有公共构造方法
// Constructor[] constructors = class1.getConstructors();
// 获取所有所有构造方法
Constructor[] constructors = class1.getDeclaredConstructors();
for (Constructor con : constructors) {
sop(con);
}
//获取无参构造方法
Constructor constructor = class1.getDeclaredConstructor();
//取消访问检查,暴力访问
constructor.setAccessible(true);
//创建对象
Object object = constructor.newInstance();
sop(object);//null::0
//获取有参构造方法
Constructor constructor2 = class1.getDeclaredConstructor(String.class);
//创建对象
Object object2 = constructor2.newInstance("张飞");
sop(object2);//张飞::0
}
/**
* @param con
*/
private static void sop(Object obj) {
System.out.println(obj);
}
}
5.4反射类的成员变量并使用:
获取所有成员:
Field[] getFields():返回包含某些Field对象的数组,表示所代表类中的成员字段。
Field[] getDeclaredFields():
获取单个成员:
Field getField(String name):返回一个Field对象,它表示此Class对象所代表的类或接口的指定公共成员字段。
Field getDeclaredField(String name):
修改成员的值:Field类的方法
set(Object obj,Object value) :将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
如:
[java] view plaincopy
Field field = clazz.getField(fieldName);//获取指定的字段值
field.setAccessible(true);//取消访问检查,也称暴力访问
field.set(obj,value);//将指定对象变量上此 Field 对象表示的字段设置为指定的新值.
示例:
[java] view plaincopy
package itcast;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
/**
* @author oyfc
*/
public class FieldDemo {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
//获取字节码文件对象
String className = "itcast.Person";
Class class1 = Class.forName(className);
//创建实例对象
Constructor constructor = class1.getConstructor(String.class,int.class);
Object object = constructor.newInstance("张非",21);
//获取所有成员变量
// Field[] fields = class1.getDeclaredFields();
// for(Field field:fields) {
// sop(field);
// }
//获取单个成员变量
Field field = class1.getField("name");
//为指定变量赋值
field.set(object, "xiaozhang");
sop(object);//xiaozhang::21
}
/**
* @param field
*/
private static void sop(Object object) {
System.out.println(object);
}
}
6、数组的反射
(1)每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组属于同一个类型,即都共享该 Class 对象。
(2)代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
(3)基本数据类型的一维数组可以被当做Object类型使用,不能当做Object[]类型使用;非基本数据类型的一维数组既可以当做Object类型使用,有可以当做object[]类型使用。
示例:
public class ReflectTest {
public static void main(String[] args) throws Exception {
int [] a1 = new int[]{1,2,3};
int [] a2 = new int[4];
int[][] a3 = new int[2][3];
String [] a4 = new String[]{"a","b","c"};
System.out.println(a1.getClass() == a2.getClass());//true
System.out.println(a1.getClass() == a4.getClass());//false
System.out.println(a1.getClass() == a3.getClass());//false
System.out.println(a1.getClass().getName());//[I
System.out.println(a1.getClass().getSuperclass().getName());//java.lang.Object
System.out.println(a4.getClass().getSuperclass().getName());//java.lang.Object
Object aObj1 = a1;
Object aObj2 = a4;
//Object[] aObj3 = a1;//错误
Object[] aObj4 = a3;
Object[] aObj5 = a4;
System.out.println(a1);//打印的是变量a1的值,不是数组的值[I@1cfb549
System.out.println(a4);
System.out.println(Arrays.asList(a1));
System.out.println(Arrays.asList(a4));
}
(4)Arrays.asList()方法处理int[]和String[]时的差异
static List asList(T… a)// 返回一个受指定数组支持的固定大小的列表。(对返回列表的更改会“直接写”到数组。)
System.out.println(Arrays.asList(a1));//[[I@1cfb549]
System.out.println(Arrays.asList(a4));//[a,b,c]
(5)Array工具类用于完成对数组的反射操作
Array 类提供了动态创建和访问 Java 数组的方法。
static Object get(Object array,int index)//返回指定数组对象中索引组件的值。如果该值是一个基本类型值,则自动将其包装在一个对象中。array - 数组,index - 索引
static int getLength(Object array) //以 int 形式返回指定数组对象的长度。
private static void printObject(Object obj) {
Class clazz = obj.getClass();
if(clazz.isArray()){
int len = Array.getLength(obj);
for(int i=0;i<len;i++){
System.out.println(Array.get(obj, i));
}
}else{
System.out.println(obj);
}
}
HashSet中的hashCode()方法示例:当从HashSet集合中查找某个对象时,java系统首先调用对象的hashCode()方法获取对象的哈希码,然后根据哈希码找到相应的存储区域,取出该存储区域内的每个元素与该对象进行equals方法比较。
注意:
(1)通常,一个类的两个实例对象用equals()方法比较的结果相等时,它们的哈希码也必须相等,反之则不成立。
(2)当一个对象被存储进HashSet集合中以后,就不能修改这个集合对象中那些参与计算哈希值的字段了,否则,对象被修改后的哈希值会改变,这样用该对象的当前引用去集合中查找对象时,会找不到,导致无法从集合中单独删除当前对象造成内存泄漏。
Collection collections = new HashSet();
ReflectPoint pt1 = new ReflectPoint(3,3);//ReflectPoint类是自定义的,包含字段x,y
ReflectPoint pt2 = new ReflectPoint(5,5);
ReflectPoint pt3 = new ReflectPoint(3,3);
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
//pt1.y = 7;//字段y参与了哈希值的运算
collections.remove(pt1);//操作之后没有删除pt1.
7、反射的作用:实现框架的功能
框架与工具类有区别,工具类被用户的类调用,而框架式调用用户提供的类。
在写框架时,用户可能还不存在,那么我写的框架程序怎么调用用户以后写的类?因为在写程序时无法知道要被调用的类名,所有在程序中无法直接new某个类的实例对象,要用到反射方式。
采用配置文件加反射的方式创建ArrayList和HashSet的实例对象。
示例:
public class ReflectTest2 { public static void main(String[] args) throws Exception{ //InputStream ips = new FileInputStream("config.properties"); //查找资源,类加载器,配置文件放在classpath的目录下 //InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties"); //相对该类包的路径,写相对路径 //InputStream ips = ReflectTest2.class.getResourceAsStream("resources/config.properties"); //在classpath的根目录下找,必须从根开始写绝对路径 InputStream ips = ReflectTest2.class.getResourceAsStream("/cn/itcast/day1/resources/config.properties"); Properties props = new Properties(); props.load(ips); ips.close(); String className = props.getProperty("className"); //Collection collections = new HashSet(); Collection collections = (Collection)Class.forName(className).newInstance(); } }