11.1类加载器
11.1.1 类的加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
加载:就是指将class文件读入内存,并为之创建一个Class对象。任何类被使用时系统都会建立一个Class对象。
连接: 验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
初始化:就是我们以前讲过的初始化步骤
11.1.2 类的初始化时机
创建类的实例
访问类的静态变量,或者为静态变量赋值
调用类的静态方法
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
初始化某个类的子类
直接使用java.exe命令来运行某个主类
11.1.3 类加载器
类加载器负责将.class文件加载到内在中,并为之生成对应的Class对象。
组成和作用:
1)Bootstrap ClassLoader 根类加载器,也被称为引导类加载器
负责Java核心类的加载,比如System,String等。在JDK中JRE的lib目录下rt.jar文件中。
2)Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。在JDK中JRE的lib目录下ext目录。
3)Sysetm ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。
11.2 反射
AVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。
11.2.1 获取字节码文件对象
Person.java
package cn.itcast_01;
public class Person {
private String name;
int age;
public String address;
public Person() {
}
private Person(String name) {
this.name = name;
}
Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
public void show() {
System.out.println("show");
}
public void method(String s) {
System.out.println("method " + s);
}
public String getString(String s, int i) {
return s + "---" + i;
}
private void function() {
System.out.println("function");
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", address=" + address
+ "]";
}
}
PersonDemo.java
package cn.itcast_01;
/*
* 反射:就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。
*
* 获取class文件对象的方式:
* 1、Object类中的getClass()方法的。想要用这种方式,必须要明确具体的类,并创建对象。
* 2、任何数据类型都具备一个静态的属性.class来获取其对应的Class对象。
* 相对简单,但是还是要明确用到类中的静态成员。还是不够扩展。
* 3、Class类中的静态方法
* public static Class forName(String className)
*
* 一般我们到底使用谁呢?
* A:自己玩 任选一种,第二种比较方便
* B:开发 第三种
* 为什么呢?因为第三种是一个字符串,而不是一个具体的类名。这样我们就可以把这样的字符串配置到配置文件中。
*/
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
// 方式1
Person p1 = new Person();
Class c1 = p1.getClass();
Person p2 = new Person();
Class c2 = p2.getClass();
System.out.println(p1 == p2);// false
System.out.println(c1 == c2);// true 因为Person类的字节码文件是同一个
// 方式2
Class c3 = Person.class;
// int.class;
// String.class;
System.out.println(c1 == c3);
// 方式3
// ClassNotFoundException
Class c4 = Class.forName("cn.itcast_01.Person");
System.out.println(c1 == c4);
}
}
运行结果:
11.2.2 获取构造方法
1.获取无参构造方法
package cn.itcast_02;
import java.lang.reflect.Constructor;
import cn.itcast_01.Person;
/*
* 通过反射获取构造方法并使用。
*/
public class ReflectDemo {
public static void main(String[] args) throws Exception {
// 获取字节码文件对象
Class c = Class.forName("cn.itcast_01.Person");
// 获取单个构造方法
// public Constructor<T> getConstructor(Class<?>... parameterTypes)
// 参数表示的是:你要获取的构造方法的构造参数个数及数据类型的class字节码文件对象
Constructor con = c.getConstructor();// 返回的是构造方法对象。
// public T newInstance(Object... initargs)
// 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
Object obj = con.newInstance();
System.out.println(obj);
Person p = (Person) obj;
p.show();
}
}
运行结果:
2.获取带参构造方法
package cn.itcast_02;
import java.lang.reflect.Constructor;
/*
* 需求:通过反射去获取该构造方法并使用:
* public Person(String name, int age, String address)
*/
public class ReflectDemo2 {
public static void main(String[] args) throws Exception {
// 获取字节码文件对象
Class c = Class.forName("cn.itcast_01.Person");
// 获取带参构造器
// public Constructor<T> getConstructor(Class<?>... parameterTypes)
Constructor con = c.getConstructor(String.class, int.class,
String.class);
// 通过带参构造方法对象来创建对象
Object obj = con.newInstance("林青霞", 27, "北京");
System.out.println(obj);
}
}
运行结果:
3. 获取私有构造方法
package cn.itcast_02;
import java.lang.reflect.Constructor;
/*
* 需求:通过反射获取私有构造方法并使用
* private Person(String name){}
*/
public class ReflectDemo3 {
public static void main(String[] args) throws Exception {
// 获取字节码文件对象
Class c = Class.forName("cn.itcast_01.Person");
// 获取私有构造方法
// NoSuchMethodException:使用getConstructor()只能得到非私有的构造方法,想要得到私有的,必须使用getConstructor();
// Constructor con = c.getConstructor(String.class);
Constructor con = c.getDeclaredConstructor(String.class);
// 用私有构造方法创建对象
// IllegalAccessException:非法的访问异常。
// Object obj = con.newInstance("风清扬");
// 暴力访问
con.setAccessible(true);// 值为true则指示反射的对象在使用时应该取消Java语言访问检查。
Object obj = con.newInstance("风清扬");
System.out.println(obj);
}
}
运行结果:
11.2.3 获取成员变量
package cn.itcast_03;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
/*
* 通过反射获取成员变量并为其赋值
*/
public class ReflectDemo {
public static void main(String[] args) throws Exception {
// 获取字节码文件对象
Class c = Class.forName("cn.itcast_01.Person");
// 通过无参构造方法创建对象
Constructor con = c.getDeclaredConstructor();
Object obj = con.newInstance();
System.out.println(obj);
// 获取单个成员变量
// 获取address并对其赋值
Field addressField = c.getField("address");
// public void set(Object obj,Object value)
// 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
addressField.set(obj, "北京");// 给obj对象的addressField字段设置值为"北京"
System.out.println(obj);
// 获取name并对其赋值
// NoSuchFieldException
// Field nameField = c.getField("name");
Field nameField = c.getDeclaredField("name");
// IllegalAccessException
nameField.setAccessible(true);
nameField.set(obj, "林青霞");
System.out.println(obj);
// 获取age并对其进行赋值
Field ageField = c.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(obj, 27);
System.out.println(obj);
}
}
运行结果:
11.2.4 获取方法
package cn.itcast_04;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ReflectDemo {
public static void main(String[] args) throws Exception {
// 获取字节码文件对象
Class c = Class.forName("cn.itcast_01.Person");
Constructor con = c.getConstructor();
Object obj = con.newInstance();
// 获取无参无返回值的方法并使用
// public void show()
// public Method getMethod(String name,Class<?>... parameterTypes)
// 第一个参数表示的方法名,第二个参数表示的是方法的参数的class类型
Method m1 = c.getMethod("show");
// obj.m1(); // 错误
// public Object invoke(Object obj,Object... args)
// 返回值是Object接收,第一个参数表示对象是谁,第二参数表示调用该方法的实际参数
m1.invoke(obj);// 调用obj对象的m1方法
System.out.println("-----");
//获取带参无返回值的方法并使用
// public void method(String s)
Method m2 = c.getMethod("method", String.class);
m2.invoke(obj, "hello");
System.out.println("-----");
//获取带参带返回值的方法并使用
// public String getString(String s, int i)
Method m3 = c.getMethod("getString", String.class, int.class);
Object objString = m3.invoke(obj, "hello", 100);
System.out.println(objString);
System.out.println("-----");
//获取私有方法并使用
// private void function()
Method m4 = c.getDeclaredMethod("function");
m4.setAccessible(true);//取消Java语言访问检查
m4.invoke(obj);
}
}
运行结果:
11.2.5 反射练习题
1. 通过配置文件运行类中的方法
Student.java
package cn.itcast.test_01;
public class Student {
public void love() {
System.out.println("爱生活,爱学习");
}
}
Teacher.java
package cn.itcast.test_01;
public class Teacher {
public void love() {
System.out.println("爱生活,爱教学");
}
}
Worker.java
package cn.itcast.test_01;
public class Worker {
public void love() {
System.out.println("爱生活,爱工作");
}
}
class.txt
className=cn.itcast.test_01.Worker
methodName=love
Test.java
package cn.itcast.test_01;
import java.io.FileReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;
/*
* 通过配置文件运行类中的方法
*
* 反射:
* 需要有配置文件配合使用。
* 用class.txt代替。
* className
* methodName
*/
public class Test {
public static void main(String[] args) throws Exception {
// 加载键值对数据
Properties prop = new Properties();
FileReader fr = new FileReader("class.txt");
prop.load(fr);
fr.close();
// 获取数据
String className = prop.getProperty("className");
String methodName = prop.getProperty("methodName");
// 反射
Class c = Class.forName(className);
Constructor con = c.getDeclaredConstructor();
Object obj = con.newInstance();
// 调用方法
Method m = c.getDeclaredMethod("love");
m.invoke(obj);
}
}
运行结果:
2. 在ArrayList<Integer>集合中添加String类型数据
package cn.itcast.test_02;
import java.lang.reflect.Method;
import java.util.ArrayList;
/*
* 我给你ArrayList<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?
*/
public class ArrayListDemo {
public static void main(String[] args) throws Exception {
// 创建集合对象
ArrayList<Integer> array = new ArrayList<Integer>();
// array.add("hello");//报错
Class c = array.getClass();// 集合ArrayList的class文件对象
Method m = c.getMethod("add", Object.class);
m.invoke(array, "hello");// 调用array的add方法,传入的值是hello
m.invoke(array, "world");
m.invoke(array, "java");
System.out.println(array);
}
}
运行结果:
3. 写一个方法:public void setProperty(Object obj, String propertyName, Object value){},此方法可将obj对象中名为propertvName的属性的值设置为value。
Tool.java
package cn.itcast.test_03;
import java.lang.reflect.Field;
/*
* 写一个方法:public void setProperty(Object obj, String propertyName, Object value){},
* 此方法可将obj对象中名为propertvName的属性的值设置为value
*/
public class Tool {
public void setProperty(Object obj, String propertyName, Object value) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{
//获取字节码文件对象
Class c = obj.getClass();
//获取该对象的propertyName成员变量
Field field = c.getDeclaredField(propertyName);
//取消访问检查
field.setAccessible(true);
//给对象的成员变量赋值为指定的值
field.set(obj, value);
}
}
ToolDemo.java
package cn.itcast.test_03;
public class ToolDemo {
public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Person p = new Person();
Tool t = new Tool();
t.setProperty(p, "name","林青霞" );
t.setProperty(p, "age",27 );
System.out.println(p);
System.out.println("-----------");
Dog d = new Dog();
t.setProperty(d, "sex", '男');
t.setProperty(d, "price", 12.34f);
System.out.println(d);
}
}
class Dog {
char sex;
float price;
@Override
public String toString() {
return sex + "---" + price;
}
}
class Person {
private String name;
public int age;
@Override
public String toString() {
return name + "---" + age;
}
}
运行结果:
11.3 动态代理
在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。我们有更强大的代理cglib
Proxy类中的方法创建动态代理类对象。
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
三个参数的解释:
ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载。
Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了。
InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上。
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke 方法来进行调用。
Object invoke(Object proxy,Method method,Object[] args)Object invoke(Object proxy,Method method,Object[] args)
三个参数的解释:
proxy:代表动态代理对象
method:代表正在执行的方法
args:代表调用目标方法时传入的实参
使用示例:
UserDao.java
package cn.itcast_06;
public interface UserDao {
public abstract void add();
public abstract void delete();
public abstract void update();
public abstract void find();
}
StudentDao.java
package cn.itcast_06;
public interface StudentDao {
public abstract void login();
public abstract void regist();
}
UserDaoImpl.java
package cn.itcast_06;
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("添加功能");
}
@Override
public void delete() {
System.out.println("删除功能");
}
@Override
public void update() {
System.out.println("修改功能");
}
@Override
public void find() {
System.out.println("查找功能");
}
}
StudentDaoImpl.java
package cn.itcast_06;
public class StudentDaoImpl implements StudentDao{
@Override
public void login() {
System.out.println("登陆功能");
}
@Override
public void regist() {
System.out.println("注册功能");
}
}
MyInvocationHandler.java
package cn.itcast_06;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object target;// 目标对象
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("权限校验");
Object result = method.invoke(target, args);
System.out.println("日志记录");
return result;// 返回的是代理对象
}
}
Test.java
package cn.itcast_06;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
UserDao ud = new UserDaoImpl();
// 使用动态代理前
ud.add();
ud.delete();
ud.update();
ud.find();
System.out.println("--------");
// 我们要创建一个动态代理对象
// Proxy类中有一个方法可以创建动态代理对象
// public static Object newProxyInstance(ClassLoader loader,Class<?>[]
// interfaces,InvocationHandler h)
// 我准备对ud对象做一个代理对象
MyInvocationHandler handler = new MyInvocationHandler(ud);
// 这里把handler与ud新生成的代理类关联起来
UserDao proxy = (UserDao) Proxy.newProxyInstance(ud.getClass()
.getClassLoader(), ud.getClass().getInterfaces(), handler);
//这里无论访问哪个方法,都会把请求转发到handler.invoke
proxy.add();
proxy.delete();
proxy.update();
proxy.find();
}
}
运行结果: