一、反射
1.反射的定义
反射是指在程序运行时动态获取程序信息和动态调用对象。
二、类的加载器
1.类的生命周期
一个类从被加载到内存在到内存中的释放一般会经历加载(加载.class文件)、连接(验证、准备、解析)、初始化、使用,卸载几个阶段。连接(验证、准备、解析)就是 常说的类的加载过程
验证:验证是连接的第一步也是为了保证我们的Java.class文件符合我们的虚拟机规定的约束条件
准备:准备的核心工作是java为类中的定义的变量(成员变量)分配内存并进行初始化。
解析:该阶段是Java虚拟机将常量池里的符号应用替换为直接引用(堆中的内存地址)
注:1.准备阶段分配内存并不包括为类的实例化对象进行分配内存,实例化对象分配内存是在对象被实例化是才会在堆中
2.为变量进行初始化的数据是为其分配其对应的数据的0值
2.类加载的分类
类加载器分为:启动类加载器(Bootstrap Classloader)、扩展类加载器(Extension ClassLoader)、引用程序类加载器(Application Classloade)、自定义类加载器(User Classloader)
启动类加载器:是主要负责加载Java的核心库
扩展类加载器:主要是负责加载Java的扩展库
应用程序类加载器:主要加载我们平时写的代码
自定义类加载器:主要用于一些特殊需求的类加载器入需要对.class文件进行加密或者要加载的类不在常规路径下需要用自定义类加载器加载类
每个类加载器都有自己的父类加载器,启动类加载器是使用c和c++来完成的所有不能被拿到
三、class对象
注:程序中每一个类和接口都有对应的class文件,报扩所有的数据类型和Java的关键字都用其对应的class文件
获取class对象的方式
1.通过数据类型.class获取class对象
Class<?> clazz=int.class;
System.out.println(clazz);
2.通过数据类型的对象.getclass获取class对象
String s1="nic";
Class<?> class1 = s1.getClass();
System.out.println(class1);
3.通过class.forName(“全限定名”)去获取class类的对象
Class<?> clazz = Class.forName("java.lang.String");
System.out.println(clazz);
4.通过类加载器对象.loadClass(“全限定名”)去获取class类的对象
Class<?> clazz=Test.class;
ClassLoader classLoader = clazz.getClassLoader();
Class<?> loadClass = classLoader.loadClass("java.lang.String");
System.out.println(loadClass);
四、获取运行时类或者接口的加载器名、全限定名、所属的包及其包名、修饰符
public class Cat {
public static void main(String[] args) {
Class<?> clazz=Cat.class;//获取类的.class文件
ClassLoader classLoader=clazz.getClassLoader();//获取类的加载器
System.out.println(classLoader);
//sun.misc.Launcher$AppClassLoader@6d06d69c
//$ 这个表示内部类 $后面的是内部类的类名
String name = clazz.getName();//获取类的全限定名
System.out.println(name);
//com.etime1.Cat
Package package1 = clazz.getPackage();//获取类所属的包
String name2 = package1.getName();//获取包的名称
System.out.println(name2);
//com.etime1
int modifiers = clazz.getModifiers();//获取类的修饰符
System.out.println(Modifier.toString(modifiers));
//利用Modifier类的toString方法输出可以看懂的修饰符
//false
System.out.println(Modifier.isPrivate(modifiers));
//利用Modifier类的isPrivate方法判断类的修饰符是否为private类型
System.out.println(Modifier.isPublic(modifiers));
//true
}
}
获取类的属性、方法、构造器
被获取的类
import java.io.Serializable;
import com.etime1.Cat;
@SuppressWarnings("all")
public class Student extends Cat implements Serializable{
private String id;
public String name;
private int age;
private static String gender="man";
//类的构造器
public Student() {
super();
// TODO Auto-generated constructor stub
}
public Student(String id, String name, int age) {
super();
this.id = id;
this.name = name;
this.age = age;
}
@SuppressWarnings("unused")//强制取消警告 用于定义定义占时没有被使用变量时不想看到警告
private Student(String name, int age) {
this.name=name;
this.age=age;
}
//属性的get和set方法
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "name="+this.name+","+"id="+this.id+",age="+this.age+",gender="+this.gender;
}
//类定义的方法
public void fun1() {
System.out.println("这是类定义的public修饰的无参方法");
}
private void fun2(String name,int age) {
System.out.println("这是类定义的public修饰的无参方法有返回值的方法");
System.out.println(name+","+age);
}
private void fun3() {
System.out.println("这是类定义的private修饰的无参无返回值的方法");
}
private int fun4(int age) {
System.out.println("这是类定义的private修饰的有参有返回值的方法");
return age;
}
private static void fun5(int age) {
System.out.println("这是静态方法");
}
}
测试类
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@SuppressWarnings("all")//抑制所有警告
public class Test {
public static void main(String[] args) throws Exception, Exception {
Class<?> clazz=Student.class;
Class<?> superclass = clazz.getSuperclass();//获取类的所继承的父类
System.out.println(superclass);
//一个类可以继承多个接口
Class<?>[] interfaces = clazz.getInterfaces();//获取类实现的接口
for( Class<?> i:interfaces) {
System.out.println(i);
}
//获取类的public修饰的属性,
Field[] fields = clazz.getFields();//获取类的public修饰的属性
for(Field i:fields) {
System.out.println(i);
String name = i.getName();
int modifiers = i.getModifiers();
String string = Modifier.toString(modifiers);
System.out.println("属性名="+name+"属性修饰符为"+string);
}
System.out.println("---------");
//.getFields()方法只能获取共有属性,如果想要用这个方法获取私有属性所被修饰的修饰符要先取消属性的安全性检查
for(Field i:fields) {
i.setAccessible(true);//取消安全性检查
String name = i.getName();
int modifiers = i.getModifiers();
String string = Modifier.toString(modifiers);
System.out.println("属性名="+name+"属性修饰符为"+string);
}
System.out.println("---------");
System.out.println("************");
//获取类中声明过得属性(包括private修饰的属性)
Field[] declaredFields = clazz.getDeclaredFields();
for(Field j:declaredFields) {
System.out.println(j);
String name = j.getName();
int modifiers = j.getModifiers();
String string = Modifier.toString(modifiers);
System.out.println("属性名="+name+"属性修饰符为"+string);
}
System.out.println("************");
//运行时获取类的public构造器
Constructor<?>[] constructors = clazz.getConstructors();
for(Constructor<?> i:constructors) {
System.out.println(i);
}
System.out.println("*************");
//获取运行时类所有声明的构造器
Constructor<?>[] constructors1 = clazz.getDeclaredConstructors();
for(Constructor<?> j:constructors1) {
System.out.println(j);
}
System.out.println("*************");
//获取类指定的构造器
Constructor<?> constructor2 = clazz.getDeclaredConstructor(String.class,int.class);
System.out.println(constructor2);
//获取构造器的形参
System.out.println(Modifier.toString(constructor2.getModifiers()));
//获取构造器的形参的类型
Class<?>[] parameterTypes = constructor2.getParameterTypes();
for(Class<?> i:parameterTypes) {
System.out.println(i);
}
System.out.println("*************");
//获取类的public修饰的方法
Method[] methods = clazz.getMethods();
for(Method i:methods) {
String string = Modifier.toString(i.getModifiers());//获取方法修饰符
Class<?> returnType = i.getReturnType();//获取方法的返回值类型
System.out.println(returnType);
//获取方法的抛出异常的类型
Class<?>[] exceptionTypes = i.getExceptionTypes();
for(Class<?> m:exceptionTypes) {
System.out.println(m);
}
Class<?>[] parameterTypes2 = i.getParameterTypes();//获取方法形参类型
for(Class<?> k:parameterTypes2) {
System.out.println(k);
}
}
//获取类所声明的方法
Method[] declaredMethods = clazz.getDeclaredMethods();
//获取指定的方法
clazz.getDeclaredMethod("setName", String.class);
/**
* 运行时创建类的对象
*/
Object object = clazz.newInstance();//调用无参构造方法创建对象
System.out.println(object);
//调用有惨构造器创建对象
//拿到有参构造器
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);//取消安全检查 。在要对类中private修饰的除拿到之外进行操作需要取消安全检查
Object newInstance = declaredConstructor.newInstance("ljx",25);
System.out.println(newInstance);
/**
* 运行时操作对象的成员变量
* 注:访问静态所修饰的字段无需传递对象
*/
//1.访问非静态的成员属性
//创造对象
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class,int.class);
constructor.setAccessible(true);
Object obj = constructor.newInstance("tom",21);
//获取类的字段
Field declaredField = clazz.getDeclaredField("age");//获取字段
declaredField.setAccessible(true);//取消安全检查
Object values = declaredField.get(obj);//获取对象的非静态字段值
System.out.println(values);
declaredField.set(obj, 32);//改变对象的非静态字段值
System.out.println(obj);
//访问对象的静态属性
Field field = clazz.getDeclaredField("gender");//获取字段
field.setAccessible(true);//取消安全检查
Object value = field.get(null);//获取对象的静态字段
System.out.println(value);
field.set(obj, "women");//改变对象的静态字段值
System.out.println(obj);
/**
* 操作对象的方法
*/
//操作对象的非静态方法:先拿到方法,方法使用对象调用
Object Instance2 = clazz.newInstance();
Method setName = clazz.getDeclaredMethod("setName", String.class);
setName.invoke(Instance2, "lxx");
Method getName = clazz.getDeclaredMethod("getName");
Object value2 = getName.invoke(Instance2);
System.out.println(value2);
//操作静态方法
Method fun5 = clazz.getDeclaredMethod("fun5",int.class);
fun5.setAccessible(true);
Object values2 = fun5.invoke(Instance2,24);
}
}
总结
1.获取无论是属性和方法还是构造器get只能获取public修饰的
2.getDeclare**是获取类中所有声明的包括private 所修饰的
3.getDeclare****也只是可以获得名称要对具体的值进行操作,
要使用.setAccessible(true)取消安全检查
4.在利用反射去创建对象时,如果使用公有的构造方法去创建对象可以直接使用class对象.newInstance去创造对象
5.若想用私有的构造方法去创建对象,那么需要先 使用class对象去先拿到构造器在利用构造器的对象去创建对象
6.在改变利用反射创建出来的对象的所属属性时,若属性是 静态的那么,直接先获取对象的字段,然后在利用字段.set()方法去设置对象的所属属性,不需要传递对象参数
7.在改变利用反射创建出来的对象的所属属性时,若属性是非静态的那么,要先获得对象的字段,再利用字段.set()方法设置对象的所属属性时需要传递对象