类加载器
概述
类初始化时机
类加载器
反射
反射概述
class
拿到class字节码文件对象
获取父类class
构造方法Constructor。
获取构造方法
调用构造方法
强制转换(一般不用)
强制访问
Field成员变量
获取所有public成员变量(自己的public和父类的public的)
获取所有成员变量(自己的所有)
获取单个的成员变量
变量赋值
强制访问私有变量
获取字段名
获取值
案例
Method方法
获取所有public方法---public,包括父类public
获取自己的所有方法(包括私有)(无父类)
获取单个public方法
获取单个方法—可以私有
暴力访问
调用方法
案例
反射案例
通过配置文件运行类中的方法(便于修改调用不同类)
ArrayList对象,添加一个字符串数据
将对象的名为propertyName的属性的值设置为value
通过用户的增删改查和学生的登录注册引出中介
类加载器和反射
类加载器
概述
- 定义:当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
- 加载
就是指将class文件读入内存,并为之创建一个Class
对象(构造方法、成员变量、成员方法)。
任何类被使用时系统都会建立一个Class对象。
- 连接
- 验证是否有正确的内部结构,并和其他类协调一致
- 准备负责为类的静态成员分配内存,并设置默认初始化值(静态的)--
静态成员是随着类的加载而加载的
- 解析将类的二进制数据中的符号引用替换为直接引用
- 初始化 就是我们以前讲过的初始化步骤
类初始化时机
- 创建类的实例(实例化)
- 访问类的静态变量,或者为静态变量赋值
- 调用类的静态方法
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
类加载器
- 类加载器概述:负责将.class文件加载到内在中,并为之生成对应的Class对象。
虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。
- 类加载器的组成
Bootstrap ClassLoader
根类加载器
Extension ClassLoader
扩展类加载器
Sysetm ClassLoader
系统类加载器
- 类加载器的作用
Bootstrap ClassLoader
根类加载器
也被称为引导类加载器,负责Java核心类的加载
比如System,String等。在JDK中JRE的lib目录下rt.jar(全部是class文件)文件中
Extension ClassLoader
扩展类加载器
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录
Sysetm ClassLoader
系统类加载器(自己写的文件)
负责在JVM启动时加载来自java命令的class
文件,以及classpath环境变量所指定的jar包和类路径
反射
反射概述
就是指将class文件读入内存,并为之创建一个
Class
对象(构造方法、成员变量、成员方法)。 任何类被使用时系统都会建立一个Class对象。
- 验证是否有正确的内部结构,并和其他类协调一致
- 准备负责为类的静态成员分配内存,并设置默认初始化值(静态的)--
静态成员是随着类的加载而加载的 - 解析将类的二进制数据中的符号引用替换为直接引用
虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。
Bootstrap ClassLoader
根类加载器Extension ClassLoader
扩展类加载器Sysetm ClassLoader
系统类加载器
Bootstrap ClassLoader
根类加载器
也被称为引导类加载器,负责Java核心类的加载
比如System,String等。在JDK中JRE的lib目录下rt.jar(全部是class文件)文件中Extension ClassLoader
扩展类加载器
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录Sysetm ClassLoader
系统类加载器(自己写的文件)
负责在JVM启动时加载来自java命令的class
文件,以及classpath环境变量所指定的jar包和类路径
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
- 反射定义:就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。
Person p = new Person();
p.使用------这不叫反射
要想这样使用,首先你必须得到class文件对象,其实也就是得到Class类的对象。
- Class类组成:
- 成员变量 Field
- 构造方法 Constructor
- 成员方法 Method
- 获取class文件对象的方式:
- Object类的getClass()方法
- 数据类型的静态属性class
- Class类中的静态方法(开发建议使用)
public static Class forName(String className)//forName
一般我们到底使用谁呢?
A:自己玩 任选一种,第二种比较方便
B:开发 第三种
为什么呢?因为第三种是一个字符串,而不是一个具体的类名。这样我们就可以把这样的字符串配置到配置文件中
class
拿到class字节码文件对象
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
// 方式1
Person p = new Person();
Class c = p.getClass(); //1.有泛型,所以有黄线
//c拿到p的字节码文件
Person p2 = new Person();
Class c2 = p2.getClass();
System.out.println(p == p2); // false
System.out.println(c == c2); // true ,他们是同一个class文件
// 方式2
Class c3 = Person.class;//2.
// int.class;
// String.class; 只要是数据类型都有class文件对象
System.out.println(c == c3); // true ,他们是同一个class文件
// 方式3
// ClassNotFoundException需要抛出异常
Class c4 = Class.forName("cn.itcast_01.Person"); //3.需要类的全部路径
System.out.println(c == c4);
}
}
注意:第三种写法容易出错,
- 可以在外侧写完了在剪切过去
- 右击类对应的绿色图标,选择复制类的全路径,就可以直接粘贴了。
获取父类class
Person p = new Person();
p.getClass().getSuperclass()
构造方法Constructor。
获取构造方法
- 所有公共构造方法,public
public Constructor[] getConstructors()
- 所有构造方法
public Constructor[] getDeclaredConstructors()
Constructor[] cons = c.getDeclaredConstructors();
- 获取单个公共构造方法
…表示几个参数都可以
public Constructor<T> getConstructor(Class<?>… parameterTypes)
Constructor con = c.getConstructor();//返回的是构造方法对象
- 获取私人构造
Constructor con = c.getDeclaredConstructor(String.class);
con.setAccessible(true);//强制访问私有方法,然后就可以和普通构造一样使用了
调用构造方法
public T newInstance(Object... initargs)
使用此 Constructor
对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。获得的是初始化的相应构造方法,及所有变量的初始化值
Object obj = con.newInstance();
Constructor con = c.getConstructor(String.class, int.class,String.class);
相当于:
Person p = new Person();
System.out.println(p);
- 获得所有构造方法,并输出
//输出的构造方法是全名称的
public class ReflectDemo {
public static void main(String[] args) throws Exception {
// 获取字节码文件对象
Class c = Class.forName("cn.itcast_01.Person");
Constructor[] cons = c.getDeclaredConstructors(); //获得所有构造方法
for (Constructor con : cons) { //遍历输出构造方法。
System.out.println(con);
}
}
}
- 无参构造
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(); //调用并获得构造方法,
System.out.println(obj);
//获得的是初始化的相应构造方法,及初始化的变量
}
}
- 带参构造
/*带参构造
* public Person(String name, int age, String address)
* Person p = new Person("林青霞",27,"北京");
* System.out.println(p);
反射的方法是:
*/
public class ReflectDemo2 {
public static void main(String[] args) throws Exception {
// 获取字节码文件对象
Class c = Class.forName("cn.itcast_01.Person");
// 获取带参构造方法对象
Constructor con = c.getConstructor(String.class, int.class, String.class);
// 通过带参构造方法对象创建对象
// public T newInstance(Object... initargs)
Object obj = con.newInstance("林青霞", 27, "北京");
System.out.println(obj);
}
}
- 私有构造
/*私有构造
* private Person(String name){}
* Person p = new Person("风清扬");
* System.out.println(p);
*/
public class ReflectDemo3 {
public static void main(String[] args) throws Exception {
// 获取字节码文件对象
Class c = Class.forName("cn.itcast_01.Person");
// 获取私有构造方法对象
// NoSuchMethodException:每个这个方法异常1:
// 原因是一开始我们使用的方法只能获取公共的,下面这种方式就可以了。
Constructor con = c.getDeclaredConstructor(String.class);
// 用该私有构造方法创建对象
// IllegalAccessException:非法的访问异常2:。
// 暴力访问
con.setAccessible(true); // 值为true则指示反射的对象在使用时应该取消Java语言访问检查。
Object obj = con.newInstance("风清扬");
System.out.println(obj);
}
}
强制转换(一般不用)
强制转换为对应的类。
可以直接使用对应方法(反射没机会这样用)
Person p = (Person)obj;
强制访问
强制访问私有构造方法,然后就可以和普通构造一样使用了
con.setAccessible(true);
Field成员变量
c是一个class
获取所有public成员变量(自己的public和父类的public的)
Field[] fields = c.getFields();
获取所有成员变量(自己的所有)
Field[] fields = c.getDeclaredFields();
获取单个的成员变量
Field addressField = c.getField("address");//获得address
变量赋值
public void set(Object obj,Object value)
addressField.set(obj, "北京");//先创建构造,在获取成员变量,才能赋值
强制访问私有变量
通过getDeclaredFields可以获取私有的变量,但是私有的无法赋值,使用这个设置true之后,就可以正常访问这个字段了(读/写)
ageField.setAccessible(true);
获取字段名
String column = field.getName();
获取值
//获取所有字段
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
//设置是否允许访问,不是修改原来的访问权限修饰词。
field.setAccessible(true);
Object value = field.get(obj);
}
案例
// Person p = new Person(); p.address = "北京"; System.out.println(p);
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();
System.out.println(obj);
// 获取单个的成员变量
// 获取address并对其赋值
Field addressField = c.getField("address");
//赋值
addressField.set(obj, "北京"); // 给obj对象的addressField字段设置值为"北京"
System.out.println(obj);
// 获取name并对其赋值
// NoSuchFieldException
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);
}
}
Method方法
获取所有public方法---public,包括父类public
Method[] getMethods
获取自己的所有方法(包括私有)(无父类)
Method[] getDeclaredMethods
获取单个public方法
Method getMethod
Method m1 = c.getMethod("show");
Method m3 = c.getMethod("getString", String.class, int.class);
获取单个方法—可以私有
getDeclaredMethod
暴力访问
method.setAccessible(true);
调用方法
public Object invoke(Object obj,Object... args)
m2.invoke(obj);
Object objString = m3.invoke(obj, "hello", 100);
//返回值是Object接收,第一个参数表示对象是谁,后面参数表示调用该方法的实际参数
案例
//Person p = new Person(); p.show();
public class ReflectDemo {
public static void main(String[] args) throws Exception {
// 获取字节码文件对象
Class c = Class.forName("cn.itcast_01.Person");
// 获取所有的方法,并输出
// Method[] methods = c.getMethods(); // 获取自己的包括父亲的公共方法
// Method[] methods = c.getDeclaredMethods(); // 获取自己的所有的方法
// for (Method method : methods) {
// System.out.println(method);
// }
Constructor con = c.getConstructor(); //获取无参构造
Object obj = con.newInstance(); //调用无参构造
// 获取单个方法并使用
// 无参方法
Method m1 = c.getMethod("show");
//调用方法
m1.invoke(obj); // 调用obj对象的m1方法
System.out.println("----------");
// 获取有参方法
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);
m4.invoke(obj);
}
}
反射案例
通过配置文件运行类中的方法(便于修改调用不同类)
需要有配置文件配合使用。
做配置文件,键是固定的,值是可变的
- 用
class.txt
代替。 - 并且你知道有两个键。
- className
- methodName
用class.txt
存需要调用的类的名称和方法,className
保存类名,methodName
保存方法名,这样修改调用的方法的时候,直接修改class.txt
文件就行了
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.getConstructor(); //通过无参构造对象
Object obj = con.newInstance();
// 调用方法
Method m = c.getMethod(methodName);
m.invoke(obj);
}
}
public class Student {
public void love() {
System.out.println("爱生活,爱Java");
}
}
public class Teacher {
public void love() {
System.out.println("爱生活,爱青霞");
}
}
ArrayList对象,添加一个字符串数据
/*
* 我给你ArrayList<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?
*/
public class ArrayListDemo {
public static void main(String[] args)
throws NoSuchMethodException, SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException { //Exception也可以
// 创建集合对象
ArrayList<Integer> array = new ArrayList<Integer>();
//源码是object类型,使用源码就可以了
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);
}
}
将对象的名为propertyName的属性的值设置为value
写一个方法,public void setProperty(Object obj, String propertyName, Object value){},此方法可将obj对象中名为propertyName的属性的值设置为value。
定义方法:强制赋值(比如原来是私有的,实例化之后不能赋值,就暴力赋值)
Final的值仍是不可变
定义:
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);
}
}
调用方法:
public class ToolDemo {
public static void main(String[] args)
throws NoSuchFieldException, SecurityException, IllegalArgumentException,
IllegalAccessException {
Person p = new Person(); //name是私有的,不能直接赋值
Tool t = new Tool();
t.setProperty(p, "name", "林青霞"); //调用方法后,直接暴力赋值
//new Tool().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;
}
}
通过用户的增删改查和学生的登录注册引出中介
用户操作接口
public interface UserDao {
public abstract void add(); //增
public abstract void delete(); //删
public abstract void update(); //改
publicabstract void find(); //查
}
接口实现类
public class UserDaoImpl implements UserDao {
@Override
publicvoid add() {
System.out.println("添加功能");
}
@Override
publicvoid delete() {
System.out.println("删除功能");
}
@Override
publicvoid update() {
System.out.println("修改功能");
}
@Override
publicvoid find() {
System.out.println("查找功能");
}
}
测试类
public class UserDaoDemo {
publicstatic void main(String[] args) {
// 基本的用户操作
UserDao ud = new UserDaoImpl(); //多态
ud.add();
ud.delete();
ud.update();
ud.find();
System.out.println("---------");
// 真实的需求应该是这个样子的:
// 在每个操作执行前:应该看看你是否有权限进行这个操作
// 谁操作的这个东西,你得给我留下记录
//在每一个方法前加权限校验,后面加日志记录(看是谁操作的)
UserDao ud2 = new UserDaoImpl2();
ud2.add();
ud2.delete();
ud2.update();
ud2.find();
// 假设我还有一个学生类,也具备这样的操作,
// 我还有一个老师类,也具备同样的操作
//那么每种类都要对应一个接口(StudentDemo),两个类(StudentDemoImpl和StudentDemoImpl2)
System.out.println("---------");
StudentDao sd = new StudentDaoImpl();
sd.login();
sd.regist();
System.out.println("---------");
// 真实的需求应该是这个样子的:
// 在每个操作执行前:应该看看你是否有权限进行这个操作
// 谁操作的这个东西,你得给我留下记录
StudentDao sd2 = new StudentDaoImpl2();
sd2.login();
sd2.regist();
}
}
添加内容
//想要有新添加的内容,不能直接修改已经完成的方法,而是新建一个类继承之前的类
public class UserDaoImpl2 implements UserDao {
@Override
publicvoid add() {
System.out.println("权限校验");
System.out.println("添加功能");
System.out.println("日志记录");
}
@Override
publicvoid delete() {
System.out.println("权限校验");
System.out.println("删除功能");
System.out.println("日志记录");
}
@Override
publicvoid update() {
System.out.println("权限校验");
System.out.println("修改功能");
System.out.println("日志记录");
}
@Override
publicvoid find() {
System.out.println("权限校验");
System.out.println("查找功能");
System.out.println("日志记录");
}
}
public interface StudentDao {
public abstract void login();
public abstract void regist();
}
//______________________________________________________
public class StudentDaoImpl implements StudentDao {
@Override
public void login() {
System.out.println("登录功能");
}
@Override
public void regist() {
System.out.println("注册功能");
}
}
//______________________________________________________
public class StudentDaoImpl2 implements StudentDao {
@Override
public void login() {
System.out.println("权限校验");
System.out.println("登录功能");
System.out.println("日志记录");
}
@Override
public void regist() {
System.out.println("权限校验");
System.out.println("注册功能");
System.out.println("日志记录");
}
}
权限和日志都是一样的,可以提取出来,作为中介---动态代理