反射、类加载与垃圾回收
反射
Java执行的过程:
Java文件——》编译——》.class字节码——运行——》类加载器(static/string常量)/字节码校验器/解释器——》OS
它允许运行中的程序对自身进行检查,并直接操作程序中的内部属性
重点语句:反射允许我们在运行时探究和使用编译时未知的类
反射使用步骤:
1、获取Class对象
1-1、通过类名获取类的Class对象——无动态性
优势:可以获取所有类型的Class对象;
劣势:没有动态效果;
例如:Class stuClass = studentBean.class;
Class stuClass =String.class;
Class intClass =int.class;
Class voidClass =void.class;
Class intArrayClass = int【】.class;
Class compClass = comparable.class;
1-2、通过实例对象获取类的Class对象
只能获取Object子类类型的getClass对象——类、数组,原因是因为getClass方法是定义在Object当中的;
可以根据传入的对象配合动态绑定,具备一定的动态效果
StudentBean stu= new studentBean();
Class stuClass0=stu.getClass();
1-3、通过类的字符串形式的类全名,获取Class对象
通过这句代码可以主动加载一个类,而加载类的时候自动执行类当中的静态代码块;
具备动态性;
本方式只支持类、接口;
try{
//写代码时可以不确定className的值,运行起来后通过外部读文件,用户输入等各种手段获取,才最终确定
String className="con.lang.bean.StudentBean";
Class stuClass1=Class.forName(className);
}catch{
}
java.lang.Sring 类全名
"con.lang.bean.StudentBean" 字符串形式的类全名
Class stuClass1=Class.forName("con.lang.bean.StudentBean"); 获取Class对象
使用情况:
如果使用场景要求动态性——编译时未知,运行时需要探究和使用的,那么放弃第一种,选择后两种;
再判断如果场景中能获取到要探究类的实例对象,选择第二种;
否则只能选择第三种,要求从程序外部(配置文件,数据库,用户输入)获取类的字符串形式类全名。
2、探究到类的信息(属性/构造/方法)
2-0、类的声明信息
String name = stuClass.getName();
String name = stuClass.getSimpleName(); 这里得到简单的类名
int classMod=stuClass.getmodifiers();
String classModStr = Modfier.toString(classMod);
String superClassName=stuClass.getSuperclass().getSimpleName();//获取父类
Class[] stuInterfaces = stuClass.getInterfaces();//获取接口
String interStr=“”;
if(stuInterfaces!=null&&stuInterfaces.length!=0){
interStr="implements";
for(int i=0;i<stuInterfaces.length;i++){
interStr+=stuInterfaces[i].getSimpleName();
if(i!=stuInterfaces.length-1){
interStr+=",";
}
}
}
System.out.println("package"+packageName+";")
System.out.println(classModStr+“class”+name
“extends”+superClassName+"{");
Class对象: package——包、 filed——属性、 Construtor ——构造、 method ——方法;
(相同名的,用的多的用导入import,用得少的用全名)
2-1、类的属性声明信息——JDK的反射当中使用Filed类型表示属性,Declared表示单复数
探究属性一共有4个常用方法:
1、filed【】 allPublicFields=stuClass.getFields();//返回所有的公共属性(包括从继承结构中获取的)
2、filed【】 allFields=stuClass.DeclaredFields();//返回所有声明的属性(只包括定义在本类身上的)
try{
3、Field thePublicField = stuClass.getField(“stuName”);//通过属性名寻找该类是否包含一个同名的公共属性(包括从继承结构获取的)
}catch{
}
4、Field theField =stuClass.getDeclaredField("stuName");//通过属性名寻找该类是否包含一个在自己本类声明的同名属性
for(Field f : allFields){
String fieldName = f.getName();//属性名
Class typeClass = f.getType();
String typeName=typeClass.getSimpleName();
String fMod=Modifier.toString(f.getModifiers());
system.out.println("\t"+fMod+""+typeName+""+f.getName()+";");
}
2-2、类的构造声明信息——JDK的反射当中使用Constructor类型表示构造,Declared表示单复数
探究构造有4个方法
1、Constructor<StudentBean>[ ] allPublicCons=stuClass.getConstructors();//返回所有的公共构造
2、Constructor<StudentBean>[ ] allCons=stuClass.getDeclaredConstructors();//f返回所有的构造(不受访问修饰符限制)
3、//获取指定的构造方法,必须传入参数列表
Constructor<StudentBean> thePublicCon=stu.Class.getConstructor(String.class, int.class, boolean.class);//返回指定某个的公共构造
4、 Constructor<StudentBean> theCon=stu.Class.getDeclaredConstructor();//获取指定的构造(不受访问修饰符限制)
for(Constructor<StudentBean> Con : allCons){
String conName=con.getName();//构造名
String conMod=Modifier.toString (con.getModifiers());//构造修饰符
Parameter 【】 params=con.getParameters();构造参数
Class【 】 exceptions= con.getExceotionTypes();抛出异常
String paramStr=“”;
if(params!=null&¶ms.length!=0){
for(int i=0;i<params.length;i++){
Parameter p=params[i]
}
}
}
2-3、类的行为声明信息——JDK的反射当中使用Method类型表示构造
Method【】 allPublicMethod=stuClass。getMethod
3、使用类的信息 属性 赋值/取值
构造 产生实例对象(这里反射就是第三种产生实例对象的方法了。1、new一个实例化对象。2、反序列化一个实例对象)
方法 调用
3-1、使用Class对象记录的类的信息
try{
Constructor con=getDeclaredConstructor(String.class, int.class, boolean.class);//通过Class对象获取到指定构造方法
StudentBean s=(StudentBean)con.newInstance("zhang2",18,true);//注意:1、要考虑con的访问修饰符能不能在这里调用到,2、考虑参数的匹配(个数、顺序、类型)
//补充:如果通过反射的方式调用公共无参构造产生实例对象,可以利用以下方简便方法:
首先,利用Class对象的newInstance方法
try{
StudentBean s=(StudentBean)stuClass.newInstance();//这是框架和工具设计人员在设计是默认使用的,也就成为了JavaBean的规范——必须要有公共无参构造
}
}
3-2、用探究到的属性,给实例对象的该属性赋值取值
StudentBean s1=new StudentBean ();
StudentBean s2=new StudentBean ();
StudentBean s3=new StudentBean ();
try{
filed f=stuClass.getFields("stuName");
f.set(s1,"古天乐");//给属性赋值;注意:1、第一个参指代给那个实例对象的属性赋值;2、注意操作书写位置是否与访问修饰符相符;3、值的类型是否与属性的类型相符;
system.out.println(f.get(s1));//从属性取值;注意:1、参数是指代从哪个实例对象中取值;2、注意书写未知是否与访问修饰符相符;3、返回值的类型是否与属性的类型相符
system.out.println(f.get(s2));
stu.setStuName("郭靖");//实例对象;是哪个方法;这个方法的实参
}
3-3、用探究到的方法,让实例对象调用该方法
类加载
类加载 ——》连接(验证--->准备--->解析) ——》初始化
student类:
相对关系——》实例对象 :类型 student/数量 不限 ——》给程序员用的
类模板对象:类型 .Class 数量只有一个 ——》给JVM用的