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);
	}
}



    运行结果:

java动态新增类属性字段_java

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();
	}
}



    运行结果:

java动态新增类属性字段_动态代理_02

    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);

	}
}



    运行结果:

java动态新增类属性字段_反射_03

   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);
	}
}



    运行结果:

java动态新增类属性字段_java动态新增类属性字段_04

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);
	}
}



    运行结果:

java动态新增类属性字段_java动态新增类属性字段_05

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);
	}
}



    运行结果:

java动态新增类属性字段_java_06

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);
	}
}



    运行结果:

java动态新增类属性字段_动态代理_07

    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);

	}
}



    运行结果:

java动态新增类属性字段_动态代理_08

    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;
	}
}



    运行结果:

java动态新增类属性字段_反射_09

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();

	}
}




    运行结果:

java动态新增类属性字段_动态代理_10