类加载器
- 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
类的加载
- 加载 就是指将class文件读入内存,并为之创建一个Class对象。任何类被使用时系统都会建立一个Class对象
- 连接 验证是否有正确的内部结构,并和其他类协调一致。准备负责为类的静态成员分配内存,并设置默认初始化值。解析将类的二进制数据中的符号引用替换为直接引用
- 初始化 就是我们以前讲过的初始化步骤(new 对象)
- 注:简单的说就是:把.class文件加载到内存里,并把这个.class文件封装成一个Class类型的对象。
类的加载时机
- 创建类的实例
- 类的静态变量,或者为静态变量赋值
- 类的静态方法
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
- 条件:运行状态
- 已知:一个类或一个对象(根本是已知.class文件)
- 结果:得到这个类或对象的所有方法和属性
Class类
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。
Modifier and Type | Method and Description |
---|---|
<U> 类<? extends U> |
asSubclass(类<U> clazz)
类 这个
类 对象来表示由指定的类对象表示的类的子类。
|
T |
cast(Object obj)
施放一个目的是通过本表示的类或接口
类 对象。
|
boolean |
desiredAssertionStatus()
如果要在调用此方法时初始化该类,则返回将分配给此类的断言状态。
|
static 类<?> |
forName(String className)
返回与给定字符串名称的类或接口相关联的
类 对象。
|
static 类<?> |
forName(String name, boolean initialize, ClassLoader loader)
使用给定的类加载器返回与给定字符串名称的类或接口相关联的
类 对象。
|
AnnotatedType[] |
getAnnotatedInterfaces()
返回一个
AnnotatedType 对象的数组,
AnnotatedType 使用类型指定由此
AnnotatedType 对象表示的实体的超级
类 。
|
AnnotatedType |
getAnnotatedSuperclass()
返回一个
AnnotatedType 对象,该对象表示使用类型来指定由此
类 对象表示的实体的
类 类。
|
<A extends Annotation> |
getAnnotation(类<A> annotationClass)
返回该元素的,如果这样的注释
,否则返回null指定类型的注释。
|
Annotation[] |
getAnnotations()
返回此元素上
存在的注释。
|
<A extends Annotation> |
getAnnotationsByType(类<A> annotationClass)
返回与此元素相关
联的注释 。
|
String |
getCanonicalName()
返回由Java语言规范定义的基础类的规范名称。
|
类<?>[] |
getClasses()
返回包含一个数组
类 表示所有的公共类和由此表示的类的成员接口的对象
类 对象。
|
ClassLoader |
getClassLoader()
返回类的类加载器。
|
类<?> |
getComponentType()
返回
类 数组的组件类型的Class。
|
Constructor<T> |
getConstructor(类<?>... parameterTypes)
返回一个
Constructor 对象,该对象反映
Constructor 对象表示的类的指定的公共
类 函数。
|
Constructor<?>[] |
getConstructors()
返回包含一个数组
Constructor 对象反射由此表示的类的所有公共构造
类 对象。
|
<A extends Annotation> |
getDeclaredAnnotation(类<A> annotationClass)
如果这样的注释
直接存在 ,则返回指定类型的元素注释,否则返回null。
|
Annotation[] |
getDeclaredAnnotations()
返回
直接存在于此元素上的注释。
|
<A extends Annotation> |
getDeclaredAnnotationsByType(类<A> annotationClass)
如果此类注释
直接存在或
间接存在,则返回该元素的注释(指定类型)。
|
类<?>[] |
getDeclaredClasses()
返回一个反映所有被这个
类 对象表示的类的成员声明的类和
类 对象的数组。
|
Constructor<T> |
getDeclaredConstructor(类<?>... parameterTypes)
返回一个
Constructor 对象,该对象反映
Constructor 对象表示的类或接口的指定
类 函数。
|
Constructor<?>[] |
getDeclaredConstructors()
返回一个反映
Constructor 对象表示的类声明的所有
Constructor 对象的数组
类 。
|
Field |
getDeclaredField(String name)
返回一个
Field 对象,它反映此表示的类或接口的指定已声明字段
类 对象。
|
Field[] |
getDeclaredFields()
返回的数组
Field 对象反映此表示的类或接口声明的所有字段
类 对象。
|
方法 |
getDeclaredMethod(String name, 类<?>... parameterTypes)
返回一个
方法 对象,它反映此表示的类或接口的指定声明的方法
类 对象。
|
方法[] |
getDeclaredMethods()
返回包含一个数组
方法 对象反射的类或接口的所有声明的方法,通过此表示
类 对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。
|
类<?> |
getDeclaringClass()
如果由此
类 对象表示的类或接口是另一个类的成员,则返回表示其声明的类的
类 对象。
|
类<?> |
getEnclosingClass()
返回底层类的即时封闭类。
|
Constructor<?> |
getEnclosingConstructor()
如果此 类 对象表示构造函数中的本地或匿名类,则返回表示底层类的立即封闭构造函数的Constructor 对象。
|
方法 |
getEnclosingMethod()
如果此 类 对象表示方法中的本地或匿名类,则返回表示基础类的即时封闭方法的方法 对象。
|
T[] |
getEnumConstants()
返回此枚举类的元素,如果此Class对象不表示枚举类型,则返回null。
|
Field |
getField(String name)
返回一个
Field 对象,它反映此表示的类或接口的指定公共成员字段
类 对象。
|
Field[] |
getFields()
返回包含一个数组
Field 对象反射由此表示的类或接口的所有可访问的公共字段
类 对象。
|
Type[] |
getGenericInterfaces()
返回
Type 表示通过由该对象所表示的类或接口直接实现的接口秒。
|
Type |
getGenericSuperclass()
返回
Type 表示此所表示的实体(类,接口,基本类型或void)的直接超类
类 。
|
类<?>[] |
getInterfaces()
确定由该对象表示的类或接口实现的接口。
|
方法 |
getMethod(String name, 类<?>... parameterTypes)
返回一个
方法 对象,它反映此表示的类或接口的指定公共成员方法
类 对象。
|
方法[] |
getMethods()
返回包含一个数组
方法 对象反射由此表示的类或接口的所有公共方法
类 对象,包括那些由类或接口和那些从超类和超接口继承的声明。
|
int |
getModifiers()
返回此类或接口的Java语言修饰符,以整数编码。
|
String |
getName()
返回由
类 对象表示的实体(类,接口,数组类,原始类型或空白)的名称,作为
String 。
|
软件包 |
getPackage()
获取此类的包。
|
ProtectionDomain |
getProtectionDomain()
返回
ProtectionDomain 。
|
URL |
getResource(String name)
查找具有给定名称的资源。
|
InputStream |
getResourceAsStream(String name)
查找具有给定名称的资源。
|
Object[] |
getSigners()
获得这个类的签名者。
|
String |
getSimpleName()
返回源代码中给出的基础类的简单名称。
|
类<? super T> |
getSuperclass()
返回
类 表示此所表示的实体(类,接口,基本类型或void)的超类
类 。
|
String |
getTypeName()
为此类型的名称返回一个内容丰富的字符串。
|
TypeVariable<类<T>>[] |
getTypeParameters()
返回一个
TypeVariable 对象的数组,它们以声明顺序表示由此
GenericDeclaration 对象表示的通用声明声明的类型变量。
|
boolean |
isAnnotation()
如果此
类 对象表示注释类型,则返回true。
|
boolean |
isAnnotationPresent(类<? extends Annotation> annotationClass)
如果此元素上
存在指定类型的注释,则返回true,否则返回false。
|
boolean |
isAnonymousClass()
返回
true 当且仅当基础类是匿名类时。
|
boolean |
isArray()
确定此
类 对象是否表示数组类。
|
boolean |
isAssignableFrom(类<?> cls)
确定由此
类 对象表示的类或接口是否与由指定的Class
类 表示的类或接口相同或是超类或
类 接口。
|
boolean |
isEnum()
当且仅当该类在源代码中被声明为枚举时才返回true。
|
boolean |
isInstance(Object obj)
确定指定的Object是否与此
Object 表示的对象分配
类 。
|
boolean |
isInterface()
确定指定
类 对象表示接口类型。
|
boolean |
isLocalClass()
返回
true 当且仅当基础类是本地类时。
|
boolean |
isMemberClass()
返回
true 当且仅当基础类是成员类时。
|
boolean |
isPrimitive()
确定指定
类 对象表示一个基本类型。
|
boolean |
isSynthetic()
如果这个类是一个合成类,返回 true ;
返回false 其他。
|
T |
newInstance()
创建由此
类 对象表示的类的新实例。
|
String |
toGenericString()
返回描述此
类 的字符串,包括有关修饰符和类型参数的信息。
|
String |
toString()
将对象转换为字符串。
|
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。
测试代码使用的Person类
public class Person {
public int num;
public String str;
public Person() {System.out.println("空参数构造方法");}
public Person(int num, String str) {
super();
this.num = num;
this.str = str;
}
private Person(String str,int num) {
super();
this.num = num;
this.str = str;
}
public String toString() {
return "Get [num=" + num + ", str=" + str + "]";
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
得到Class对象
- 通过Object类中的getClass()方法
- 通过 类名.class 获取到字节码文件对象(任意数据类型都具备一个class静态属性,看上去要比第一种方式简单)。
- 通过Class类中的方法(将类名作为字符串传递给Class类中的静态方法forName即可)。
- 第三种和前两种的区别是,前两种你必须明确Person类型.后面是指定这种类型的字符串就行.这种扩展更强.我不需要知道你的类.我只提供字符串,按照配置文件加载就可以了
- 因为一个.class文件在内存里只生成一个Class对象,所以无论那一种方法得到Class对象,得到的都是同一个对象。
public class Test {
public static void main(String[] args) throws Exception {
// 1: 通过Object类中的getObject()方法
Person p1 = new Person();
Class c1 = p1.getClass();
System.out.println("c1 = "+ c1);
// 2: 通过 类名.class 获取到字节码文件对象
Class c2 = Person.class;
System.out.println("c2 = "+ c2);
// 3: 反射中的方法
Class c3 = Class.forName("Person");// 包名.类名 如果没有包名直接写类名
System.out.println("c3 = " + c3);
}
}
获取所有权限为public的构造方法
import java.lang.reflect.Constructor;
public class Test {
public static void main(String[] args) throws Exception {
Class c = Class.forName("Person");
//使用class文件对象,获取类中的构造方法
// Constructor[] getConstructors() 获取class文件对象中的所有公共的构造方法
Constructor[] cons = c.getConstructors();
for(Constructor con : cons){
System.out.println(con);
}
}
}
获取public权限对应参数的构造方法并使用
- 无参数构造方法
public class Test {
public static void main(String[] args) throws Exception {
Class c = Class.forName("Person");
//获取指定的构造方法 如果getConstructor()不写参数,就获得无参数构造方法
Constructor con = c.getConstructor();
//运行空参数构造方法,Constructor类方法 newInstance()运行获取到的构造方法
Object obj = con.newInstance();
}
}
- 有参数构造方法
public class Test {
public static void main(String[] args) throws Exception {
Class c = Class.forName("Person");
//获取指定的有参数构造方法
Constructor con = c.getConstructor(int.class,String.class);
//运行有参数构造方法,Constructor类方法 newInstance()运行获取到的构造方法
Object obj = con.newInstance(1,"a");
System.out.println(obj.toString());
}
}
- 获取无参构造方法并使用快捷方式
public class Test {
public static void main(String[] args) throws Exception {
Class c = Class.forName("Person");
// Class类中定义方法, T newInstance() 直接创建被反射类的对象实例
Object obj = c.newInstance();
System.out.println(obj);
}
}
获取所有权限参构造方法
import java.lang.reflect.Constructor;
public class Test {
public static void main(String[] args) throws Exception {
Class c = Class.forName("Person");
//Constructor[] getDeclaredConstructors()获取所有的构造方法,包括私有的
Constructor[] cons = c.getDeclaredConstructors();
for(Constructor con : cons){
System.out.println(con);
}
}
}
获取所有权限对应参数参构造方法并使用
import java.lang.reflect.Constructor;
public class Test {
public static void main(String[] args) throws Exception {
Class c = Class.forName("Person");
//Constructor getDeclaredConstructor(Class...c)获取到指定参数列表的构造方法
Constructor con = c.getDeclaredConstructor(String.class,int.class);
//Constructor类,父类AccessibleObject,定义方法setAccessible(boolean b)
con.setAccessible(true);
Object obj = con.newInstance("lisi",18);
System.out.println(obj);
}
}
获取权限为public的成员属性并修改
import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) throws Exception {
Class c = Class.forName("Person");
//获取成员变量 Class类的方法 getFields() class文件中的所有公共的成员变量
//返回值是Field[] Field类描述成员变量对象的类
Field[] fields = c.getFields();
for(Field f : fields){
System.out.println(f);
}
}
}
- 获取成员属性并修改
import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) throws Exception {
Class c = Class.forName("Person");
Object obj = c.newInstance();
//获取指定的成员变量 String str
//Class类的方法 Field getField(传递字符串类型的变量名) 获取指定的成员变量
Field field = c.getField("str");
//Field类的方法 void set(Object obj, Object value) ,修改成员变量的值
//Object obj 必须有对象的支持, Object value 修改后的值
field.set(obj,"王五");
System.out.println(obj);
}
}
- 如果想得到所有的成员变量(包括私有的,如果要进行修改私有成员变量,要先进行public void setAccessible(boolean flag) 并且设置为true)
- public Field getDeclaredField(String name) 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
- public Field[] getDeclaredFields() 返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
获取权限为public的成员方法
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws Exception {
Class c = Class.forName("Person");
Object obj = c.newInstance();
//获取class对象中的成员方法
// Method[] getMethods()获取的是class文件中的所有公共成员方法,包括继承的
// Method类是描述成员方法的对象
Method[] methods = c.getMethods();
for(Method m : methods){
System.out.println(m);
}
}
}
- 获取空参数成员方法并运行
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws Exception {
Class c = Class.forName("Person");
Object obj = c.newInstance();
//获取指定的方法toString运行
// Method getMethod(String methodName,Class...c)
// methodName获取的方法名 c 方法的参数列表
Method method = c.getMethod("toString");
//使用Method类中的方法,运行获取到的方法toString
//Object invoke(Object obj, Object...o)
System.out.println(method.invoke(obj));
}
}
- 获取有参数成员方法并运行
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws Exception {
Class c = Class.forName("Person");
Object obj = c.newInstance();
//获取指定的方法setNum运行
// Method getMethod(String methodName,Class...c)
// methodName获取的方法名 c 方法的参数列表
Method method = c.getMethod("setNum",int.class);
//使用Method类中的方法,运行获取到的方法setNum
//Object invoke(Object obj, Object...o)
method.invoke(obj,1);
//验证是否成功
method = c.getMethod("toString");
System.out.println(method.invoke(obj));
}
}
- 得到全部的成员方法(包括私有的,如果要使用私有成员方法,要先进行public void setAccessible(boolean flag)并且设置为true)
- public Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
- public Method[] getDeclaredMethods() 返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
泛型擦除
- 伪泛型:在编译后的.class文件里面是没有泛型的。类型为Object。
- 用反射的方法绕过编译,得到Class文件对象,直接调用add方法。
import java.lang.reflect.Method;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) throws Exception {
ArrayList<String> array = new ArrayList<String>();
array.add("a");
//反射方式,获取出集合ArrayList类的class文件对象
Class c = array.getClass();
//获取ArrayList.class文件中的方法add
Method method = c.getMethod("add",Object.class);
//使用invoke运行ArrayList方法add
method.invoke(array, 150);
method.invoke(array, 1500);
method.invoke(array, 15000);
System.out.println(array);
}
}
通过配置文件来决定运行的步骤
- 通过配置文件得到类名和要运行的方法名,用反射的操作类名得到对象和调用方法
- 准备配置文件,键值对
- IO流读取配置文件 Reader
- 文件中的键值对存储到集合中 Properties 集合保存的键值对,就是类名和方法名
- 反射获取指定类的class文件对象
- class文件对象,获取指定的方法
- 运行方法
import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.Properties;
public class Test {
public static void main(String[] args) throws Exception {
//IO流读取配置文件
FileReader r = new FileReader("src\\config.properties");
//创建集合对象
Properties pro = new Properties();
//调用集合方法load,传递流对象
pro.load(r);
r.close();
//通过键获取值
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//反射获取指定类的class文件对象
Class c = Class.forName(className);
Object obj = c.newInstance();
//获取指定的方法名
Method method = c.getMethod(methodName);
System.out.println(method.invoke(obj));
}
}
// config.properties内容
/*
className=Person
methodName=toString
*/
最后一次更新于2019-09-26 16:26
0 条评论