1:类加载器(了解)
(1)什么是类的加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
一个类在加载过程中的三部曲:
1.加载
就是指将class文件读入内存,并为之创建一个Class对象.
任何类被使用时系统都会建立一个Class对象。
2.连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
3.初始化 就是我们以前讲过的初始化步骤
(2)类的加载时机(掌握)
2.1 创建类的实例
2.2 访问类的静态变量,或者为静态变量赋值
2.3 调用类的静态方法
2.4 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
2.5 初始化某个类的子类
2.6 直接使用java.exe命令来运行某个主类
(3)加载器分类
3.1 类加载起的作用?
负责将.class文件加载到内在中,并为之生成对应的Class对象。
3.2 类加载器的分类
Bootstrap ClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载,比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
Extension ClassLoader 扩展类加载器,负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录Sysetm ClassLoader 系统类加载器,负责在JVM启动时加载来自java命令的class文件
2:反射(掌握)
Student.java–Student.class(字节码文件)–看成一个对象,这个对象就叫字节码文件对象–对应的类Class
什么是反射?(画图描述)
答:通过字节码文件对象去使用成员。
(1)获取字节码文件对象的三种方式:
A:Object类的getClass()方法
B:数据类型的静态class属性
C:Class类的静态方法forName()
注意:在平常写案例的时候,我们直接使用第二种最方便。但是实际开发中,我们一般用的都是第三种。是因为第三种接收的是一个字符串类型的参数,我们可以把这个参数作为配置文件的内容进行配置,这样就实现了一个变化的内容。
案例:使用上述三种方式获取类的Class对象。
Person.java
package com.study_01;
public class Person {
//创建三个成员变量
String name;
public int age;
private String address;
//创建几个构造方法
public Person(){}
Person (String name){
this.name=name;
}
protected Person(String name,int age){
this.name=name;
this.age=age;
}
private Person(String name,int age,String address){
this.name=name;
this.age=age;
this.address=address;
}
//创建几个成员方法
public void method(){
System.out.println("method");
}
void function(int price){
System.out.println(price);
}
private void show(String hushand,String wife){
System.out.println(hushand+"--"+wife);
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", address=" + address
+ "]";
}
}
RedlectDemo.java
这里写代码片package com.study_01;
/**
* (1)获取字节码文件对象的三种方式:
A:Object类的getClass()方法
B:数据类型的静态class属性
C:Class类的静态方法forName()
* @author dell
*
*/
public class ReflectDemo {
public static void main(String[] args) throws Exception {
Person p1 = new Person();
Person p2 = new Person();
//A:Object类的getClass()方法
Class c1 = p1.getClass();
Class c2 = p2.getClass();
System.out.println(p1==p2);
System.out.println(c1==c2);
//每一个类会对应一个字节码文件对象,而这个字节码文件对象就是这个类的原型,每一个类有且仅有一个字节码文件对象
System.out.println("--------------");
//B:数据类型的静态class属性
Class c3=Person.class;
System.out.println(c3==c2);
System.out.println("--------------");
//C:Class类的静态方法forName(),在这里所说的类名是全类名(带包名的类名)
Class c4=Class.forName("com.study_01.Person");
System.out.println(c4==c3);
}
}
(2)反射的使用步骤
Class:
成员变量 Field
构造方法 Constructor
成员方法 Method
反射:
class字节码文件对象 – 去得到对应的成员对象 – 通过该成员的对象调用方法使用,通过反射获取构造方法并使用
(3)案例:
1.通过反射获取构造方法
public Constructor[] getConstructors() 获取公共的构造方法
public Constructor[] getDeclaredConstructors() 获取所有的构造方法(包括私有)
public Constructor getConstructor(Class… parameterTypes) 根据构造参数获取公共的指定构造
2.通过反射获取构造方法并创建对象
public T newInstance(Object… initargs)
ConstructorDemo.java
package com.study_02;
/**
* 我们现在已经有了一个类对应的字节码文件对象。
*
* 构造方法--封装成了一个对象--Constructor
* 成员变量--封装成了一个对象--Field
* 成员方法--封装成了一个对象--Method
*
1.通过反射获取构造方法
public Constructor[] getConstructors() 获取公共的构造方法
public Constructor[] getDeclaredConstructors() 获取所有的构造方法(包括私有)
public Constructor getConstructor(Class... parameterTypes) 根据构造参数获取公共的指定构造
public Constructor getDeclaredConstructor(Class<?>... parameterTypes) 根据构造参数获取指定构造(包括私有,但是私有在使用的时候需要取消访问限制)
2.通过反射获取构造方法并创建对象
public T newInstance(Object... initargs)
*/
import java.lang.reflect.Constructor;
public class ConstructorDemo {
public static void main(String[] args) throws Exception {
//获取文件的字节码文件对象
Class<?> c1 = Class.forName("com.study_01.Person");
//获取person类公有的构造方法
Constructor[] cons = c1.getConstructors();
//遍历
for (Constructor<?> con : cons) {
System.out.println(con);
}
System.out.println("----------------");
//获取Person类所有的构造方法
Constructor[] dcons = c1.getDeclaredConstructors();
//遍历
for (Constructor<?> con : dcons) {
System.out.println(con);
}
System.out.println("----------------");
//获取公有的指定构造(此处为无参构造)
Constructor cons2 = c1.getConstructor();
System.out.println(cons2);
//通过此构造来创建对象
Object obj = cons2.newInstance();
System.out.println(obj);
System.out.println("----------------");
//获取Person类私有的构造方法,
//获取构造器的时候,传入的什么参数,在调用获取到的这个构造方法对象的时候也就需要传入什么类型的参数
Constructor cons3 = c1.getDeclaredConstructor(String.class,int.class,String.class);
System.out.println(cons3);
//取消访问权限检测
cons3.setAccessible(true);
Object obj2 = cons3.newInstance("文章",34,"中国");
System.out.println(obj2);
//获取Person类受保护的构造方法
Constructor<?> cons4 = c1.getDeclaredConstructor(String.class,int.class);
System.out.println(cons4);
//取消权限访问检测
cons4.setAccessible(true);
Object obj3 = cons4.newInstance("谢娜",30);
System.out.println(obj3);
}
}
其中Person.java同上,这里不在重复粘贴。
3.通过反射获取成员变量并使用
public Field[] getFields()获取公有的成员变量
public Field[] getDeclaredFields()获取全部的成员变量,包括私有
public Field getDeclaredField(String name) 传入变量名称返回指定的成员变量对象,包括私有
public Field getField(String name)传入变量名称返回指定的成员变量对象,仅可获取共有的
public void set(Object obj,Object value)给一个对象的一个字段设置一个值
FieldDemo.java
package com.study_03;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
/**
3.通过反射获取成员变量并使用
public Field[] getFields()获取公有的成员变量
public Field[] getDeclaredFields()获取全部的成员变量,包括私有
public Field getDeclaredField(String name) 传入变量名称返回指定的成员变量对象,包括私有
public Field getField(String name)传入变量名称返回指定的成员变量对象,仅可获取共有的
public void set(Object obj,Object value) 给一个对象的一个字段设置一个值
*
*/
public class FileldDemo {
public static void main(String[] args) throws Exception {
//获取Person类对应的字节码文件对象
Class c = Class.forName("com.study_01.Person");
//利用反射获取一个Person对象
Constructor con = c.getConstructor();
Object obj = con.newInstance();
//获取公有的成员变量
Field[] f1 = c.getFields();
for (Field field : f1) {
System.out.println(field);
}
System.out.println("---------------");
//获取全部的成员变量
Field[] f2 = c.getDeclaredFields();
for (Field field : f2) {
System.out.println(field);
}
System.out.println("-----------------");
//传入变量名称返回指定的成员变量对象,包括私有,设置姓名
Field f3 = c.getDeclaredField("name");
//取消权限访问检测
f3.setAccessible(true);
f3.set(obj, "谢娜");
//设置年龄,传入变量名称返回指定的成员变量对象,仅可获取公有的
Field f4 = c.getField("age");
f4.setAccessible(true);
f4.set(obj, 30);
//传入变量名称返回指定的成员变量对象,包括私有,设置地址
Field f5 = c.getDeclaredField("address");
f5.setAccessible(true);
f5.set(obj, "湖南");
System.out.println(obj);
}
}
其中Person.java同上,这里不在重复粘贴。
4.通过反射获取成员方法并使用
public Method[] getMethods()获取所有公共成员方法
public Method[] getDeclaredMethods()获取所有成员方法,包括私有
public Method getMethod(String name, Class
package com.study_04;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
4.通过反射获取成员方法并使用
public Method[] getMethods()获取所有公共成员方法
public Method[] getDeclaredMethods()
public Method getMethod(String name, Class<?>... parameterTypes)参数一:方法名 参数二:方法参数类型.class 获取指定的公共方法
public Method getDeclaredMethod(String name,Class<?>... parameterTypes)参数一:方法名 参数二:方法参数类型.class 获取指定的方法,包括私有
Object invoke(Object obj, Object... args) 让某一个对象使用这个方法,并且传入参数
*
*/
public class MethodDemo {
public static void main(String[] args) throws Exception {
//创建字节码文件对象
Class<?> c = Class.forName("com.study_01.Person");
//利用反射的方式创建一个Person对象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
//获取所有公共成员方法
Method[] methods = c.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("------------------------");
//获取所有成员方法,包括私有
Method[] methods2 = c.getDeclaredMethods();
for (Method method : methods2) {
System.out.println(method);
}
System.out.println("-------------------------");
//获取指定的公共方法
Method method = c.getMethod("method");
System.out.println(method);
//使用m2这个成员方法的对象
method.invoke(obj);
System.out.println("-------------------------");
Method method2 = c.getDeclaredMethod("function", int.class);
//取消权限访问限制
method2.setAccessible(true);
method2.invoke(obj, 2000);
}
}
其中Person.java同上,这里不在重复粘贴。