类加载,反射
类加载器
类的加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初 始化三步来实现对这个类进行初始化。
加载
就是指将 class 文件读入内存,并为之创建一个 Class 对象。 任何类被使用时系统都会建立一个 Class 对象
连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用初始化
初始化阶段,虚拟机负责对类的变量进行初始化
声明类变量是指定初始值
使用静态初始化块为类变量指定初始值
使用:正常使用
卸载:GC(垃圾回收机制)把无用对象从内存中卸载。
Class 类的初始化时机
Java 虚拟机只有在程序首次主动使用一个类或接口的时候才会初始化它。只有 6 种活动被看 作是程序对类和接口的主动使用:
- 创建类的实例
- 调用类的静态方法
- 访问类或接口的静态变量,或者为静态变量赋值
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class 对象
- 初始化某个类的子类
- 直接使用 java.exe 命令来运行某个主类
类加载器
类加载器主要负责将.class 文件加载到内存中,并为之生成对应的 Class 对象。
类加载器的组成
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 包和类路径。
反射
JAVA 反射机制是在运行状态中:
- 对于任意一个类,都能够知道这个类的所有属性和方法;
- 对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。
Class类
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是 Class 类中的 方法,所以先要获取到每一个字节码文件对应的 Class 类型的对象。
获取 Class 对象三种方式
方式一:调用 Object 类的 getClass():任何类都会继承此方法
Personp=newPerson();
Classc=p.getClass()方式二:调用某个类的
class
属性来获取该类对呀的Class
对象Classs=Student.class
方式三:调用 Class 类的静态方法 forName(StringclazzName),参数的值是某个类的全限 定类名(必须添加完整包名)
Classc3=Class.forName(“com.jf.weidong.Person”);
通过这个 Class 对象,可以获取 Student 类内部的成员属性、构造方法、成员方法的一些信 息,并能够调用它们
示例代码
public class Test01 {
public static void main(String[] args) throws ClassNotFoundException {
// TODO Auto-generated method stub
/*
* 获取 Class 对象三种方式
*/
Student stu = new Student();
Class c1 = stu.getClass();
System.out.println(c1);
Class c2 = Student.class;
System.out.println(c2);
Class c3 = Class.forName("classtry.Student");
System.out.println(c3);
}
}
反射操作构造方法
通过反射获取构造方法
在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行 表示。其中,构造方法使用类 Constructor 表示。可通过 Class 类中提供的方法获取构造方 法:
- 获取单个
Constructor getConstructor(Class … parameterTypes):获取指定的”公有构 造方法”
Constructor getDeclaredConstructor(Class … parameterTypes):获取指定的 构造方法(包括私有的)
批量获取构造方法
Constructor[] getConstructors() :获取所有的”公有构造方法”
Constructor[] getDeclaredConstructors() :获取全部(包括私有)的构造方法暴力访问:如果想要访问私有成员,需要设置暴力访问
Constructor 的 setAccessible(true):不进行权限检查
示例代码
public class Test02 {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
//获取单个
// Constructor getConstructor(Class ... parameterTypes):获取指定的"公有构 造方法"
Class c = Class.forName("classtry.Student");
Constructor con1 = c.getConstructor(String.class,int.class);
System.out.println(con1);
System.out.println("=============================");
// Constructor getDeclaredConstructor(Class ... parameterTypes):获取指定的 构造方法(包括私有的)
Constructor con2 = c.getDeclaredConstructor(String.class);
System.out.println(con2);
System.out.println("=============================");
// 批量获取构造方法
// Constructor[] getConstructors() :获取所有的"公有构造方法"
Constructor[] con3 = c.getConstructors();
for (Constructor c1 : con3) {
System.out.println(c1);
}
System.out.println("==============================");
// Constructor[] getDeclaredConstructors() :获取全部(包括私有)的构造方法
Constructor[] con4 = c.getDeclaredConstructors();
for (Constructor c2 : con4) {
System.out.println(c2);
}
}
}
通过反射方式创建对象(共有构造)
- 步骤如下
- 获取到 Class 对象
- 获取指定的构造方法
- 通过构造方法类 Constructor 中的方法,创建对象
publicTnewInstance(Object…initargs)
示例代码
public class Test03 {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
//publicTnewInstance(Object...initargs)
Class c = Class.forName("classtry.Student");
Constructor con1 = c.getConstructor();
Object obj1 = con1.newInstance();
System.out.println(obj1);
System.out.println("=========================");
Constructor con2 = c.getConstructor(String.class,int.class);
Object obj2 = con2.newInstance("张三",21);
System.out.println(obj2);
System.out.println("=========================");
Constructor con3 = c.getDeclaredConstructor(String.class,int.class,String.class);
Object obj3 = con3.newInstance("李四",22,"中国");
System.out.println(obj3);
}
}
通过反射方式创建对象(私有构造)
AccessibleObject 类是 Field、Method 和 Constructor 对象的父类。它提供了将反射的 对象标记为在使用时取消默认 Java 语言访问控制检查的能力。 对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、 Method 或 Constructor 对象来设置或获取字段、调用方法,或者创建和初始化类的新实例 的时候,会执行访问检查。常用方法如下:public void setAccessible(boolean flag) throws SecurityException
参数值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。参数值 为 false 则指示反射的对象应该实施 Java 语言访问检查。
获取私有构造方法,步骤如下:
- 获取到 Class 对象
- 获取指定的构造方法
- 暴力访问, 通过 setAccessible(booleanflag)方法
- 通过构造方法类 Constructor 中的方法,创建对象
publicTnewInstance(Object…initargs)
示例代码
public class Test04 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
// TODO Auto-generated method stub
Class c = Class.forName("classtry.Student");
Constructor con1 = c.getDeclaredConstructor(String.class);
con1.setAccessible(true);//不加这一行会报错!!!
Object obj1 = con1.newInstance("李四");
System.out.println(obj1);
}
}
反射操作属性
通过反射获取成员变量并使用
在反射机制中,把类中的成员变量使用类 Field 表示。可通过 Class 类中提供的方法获取成 员变量:
获取单个成员变量
publicFieldgetField(Stringname) 获取指定的 public 修饰的变量
publicFieldgetDeclaredField(Stringname) 获取指定的任意变量获取多个成员变量
publicField[]getFields() 获取所有 public 修饰的变量
publicField[]getDeclaredFields() 获取所有的变量 (包含私有)
示例代码
public class Test05 {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
/*
* - 获取单个成员变量
* > public Field getField(Stringname) 获取指定的 public 修饰的变量
* > public Field getDeclaredField(Stringname) 获取指定的任意变量
*/
Class c = Class.forName("classtry.Student");
Field name = c.getField("name");
System.out.println(name);
Field adress = c.getDeclaredField("adress");
System.out.println(adress);
System.out.println("==============================");
/*- 获取多个成员变量
*> public Field[] getFields() 获取所有 public 修饰的变量
*> public Field[] getDeclaredFields() 获取所有的变量 (包含私有)
*
*/
Field[] f1 = c.getFields();
for (Field ff1 : f1) {
System.out.println(ff1);
}
System.out.println("==============================");
Field[] f2 = c.getDeclaredFields();
for (Field ff2 : f2) {
System.out.println(ff2);
}
}
}
通过反射,获取指定的成员变量,并赋值
获取成员变量,步骤如下:
1. 获取 Class 对象
2. 获取构造方法
3. 通过构造方法,创建对象
4. 获取指定的成员变量(私有成员变量,通过 setAccessible(booleanflag)方法暴力访问)
5. 通过方法,给指定对象的指定成员变量赋值或者获取值
- 方法介绍
publicvoidset(Objectobj,Objectvalue):在指定对象 obj 中,将此 Field 对象表示的成员 变量设置为指定的新值
publicObjectget(Objectobj):返回指定对象 obj 中,此 Field 对象表示的成员变量的值
示例代码
public class Test06 {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
/*
* public void set(Objectobj,Objectvalue):在指定对象 obj 中,
* 将此 Field 对象表示的成员 变量设置为指定的新值
* public Object get(Objectobj):返回指定对象 obj 中,此 Field 对象表示的成员变量的值
*/
Class c = Class.forName("classtry.Student");
Object obj = c.getConstructor().newInstance();
Field name = c.getField("name");
name.set(obj, "张三");
System.out.println(obj);
System.out.println("========================");
Object nameString = name.get(obj);
if(nameString instanceof String){
System.out.println("张三");
}else{
System.out.println("李四");
}
}
}
反射操作成员方法
在反射机制中,把类中的成员方法使用类 Method 表示。可通过 Class 类中提供的方法获取 成员- 获取多个
Method[]getMethods():获取所有”公有方法”(包括继承的)
Method[]getDeclaredMethods():获取所有成员方法(包括私有)
- 获取单个成员方法
MethodgetMethod(Stringname,Class…parameterTypes):获取指定的”公有方法”
MethodgetDeclaredMethod(Stringname,Class…parameterTypes):获取指定的方法,包括 私有的
- 调用方法:(注意:1.要先创建此类的对象;2.调用私有方法前,要先设置暴力访问)
Method
Objectinvoke(Objectobj,Object…args):调用 Method 所代表的方法
返回值:此 Method 对象调用所代表的方法,所获取的返回值;
形参:
obj:方法所属对象;
args:Method 所代表的那个方法的实参列表
示例代码
public class Test07 {
/**
* 反射操作成员方法
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
/*
* 获取多个
* Method[]getMethods():获取所有"公有方法"(包括继承的)
* Method[]getDeclaredMethods():获取所有成员方法(包括私有)
*/
Class c = Class.forName("classtry.Student");
Object obj = c.getConstructor().newInstance();
Constructor con1 = c.getDeclaredConstructor();
// con1.setAccessible(true);
Object obj2 = con1.newInstance();
Method[] m1 = c.getMethods();
for (Method method1 : m1) {
System.out.println(method1);
}
System.out.println("===============================");
Method[] m2 = c.getDeclaredMethods();
for (Method method2 : m2) {
System.out.println(method2);
}
System.out.println("===============================");
/*
* 获取单个成员方法
* MethodgetMethod(Stringname,Class...parameterTypes):获取指定的"公有方法"
* MethodgetDeclaredMethod(Stringname,Class...parameterTypes):
* 获取指定的方法,包括 私有的
*/
Method m3 = c.getMethod("eat");
m3.invoke(obj);
Method m4 = c.getMethod("num",int.class);
m4.invoke(obj, 3);
Method m5 = c.getMethod("nums", int.class);
Object result = m5.invoke(obj, 1);
System.out.println(result);
System.out.println("===============================");
Method m6 = c.getDeclaredMethod("learn",null);
m6.setAccessible(true);
m6.invoke(obj2);
}
}
通过反射创建对象,并调用指定的方法
执行找到的方法
publicObjectinvoke(Objectobj, Object…args)
执行指定对象 obj 中,当前 Method 对象所代表的方法,方法要传入的参数通过 args 指定。
示例代码
public class Test08 {
/**
* 通过反射创建对象,并调用指定的方法
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
/*执行找到的方法
* public Object invoke(Objectobj, Object...args)
* 执行指定对象 obj 中,当前 Method 对象所代表的方法,方法要传入的参数通过 args 指定。
*/
Class c = Class.forName("classtry.Student");
Constructor con = c.getDeclaredConstructor();
// con.setAccessible(true);
Object obj = con.newInstance();
Method m1 = c.getMethod("eat", null);
m1.invoke(obj);
Method m2 = c.getDeclaredMethod("study", null);
m2.setAccessible(true);
m2.invoke(obj);
}
}
反射练习
反射运行配置文件内容
Properties 介绍
Properties 继承自 Hashtable,说明其是以键与值的方式存在的,只是 Properties 的 键与值都是必须是字符串类型的数据。Properties 常被用于配置文件的写入与读取, 配置文件中记录着程序的各项参数信息,使用程序的用户可以自定义某些参数,以达到 软件的个性化。通过反射读取配置文件,可以不改变主程序代码,只要修改配置文件就可以操作不同的 对象执行不同的功能。
示例代码
public class Demo {
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
// System.out.println(Test02.class);
Class c = Class.forName(porties("className"));
Object obj = c.getConstructor().newInstance();
Method m1 = c.getMethod(porties("methodName"));
m1.invoke(obj);
}
public static String porties(String key) throws Exception{
Properties p = new Properties();
FileReader fr = new FileReader("protiesty.properties");
p.load(fr);
fr.close();
return p.getProperty(key);
}
}
public class Test01 {
public void show1(){
System.out.println("这是方法show1");
}
}
public class Test02 {
public void show2(){
System.out.println("这是方法show2");
}
}
//配置文件中
className = classtrain.Test02
methodName = show2
通过反射越过泛型类型检查
通过反射越过泛型检查,定义一个具有 String 泛型的集合,要求成功向集合中添加一个 int 数据
示例代码
public class Demo2 {
public static void main(String[] args) throws Exception, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
// TODO Auto-generated method stub
List<String> list = new ArrayList<>();
list.add("zhangsan");
list.add("lisi");
//list.add(12345);//该行报错
Class c = list.getClass();
//Object obj = c.getConstructor().newInstance();
Method m1 = c.getMethod("add", Object.class);
m1.invoke(list, 12435);
for (Object str : list) {
System.out.println(str);
}
}
}
通用设置属性的方法
通过反射写一个通用的方法,能够设置某个对象的某个属性为指定的值
示例代码
public class Demo3 {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Cat c = new Cat();
usual(c, "name", "123456");
c.show();
}
public static void usual(Object kindName,String filedName,Object value) throws Exception{
Class c = kindName.getClass();
Field fname = c.getDeclaredField(filedName);
fname.setAccessible(true);
fname.set(kindName, value);
}
}
public class Cat {
private String name;
public void show() {
// TODO Auto-generated method stub
System.out.println("瞎、。。。叫"+this.name);
}
}