一、类的加载
1.类的加载概述
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
- 加载:就是指将class文件读入内存,并为之创建一个Class对象。任何类被使用时系统都会建立一个Class对象。
- 连接
验证 : 是否有正确的内部结构,并和其他类协调一致
准备 : 负责为类的静态成员分配内存,并设置默认初始化值
解析: 把类中的符号引用转换为直接引用 - 初始化
2.类的加载时机
创建类的实例
访问类的静态变量,或者为静态变量赋值
调用类的静态方法
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
初始化某个类的子类
直接使用java.exe命令来运行某个主类
二、类加载器
1.类加载器的概述
负责将.class文件加载到内存中,并为之生成对应的Class对象。
2.类加载器的分类和作用
- Bootstrap ClassLoader 根类加载器:也被称为引导类加载器,负责Java核心类的加载。在JDK中JRE的lib目录下rt.jar文件中。
- Extension ClassLoader 扩展类加载器:负责JRE的扩展目录中jar包的加载。在JDK中JRE的lib目录下ext目录。
- Sysetm ClassLoader 系统类加载器:负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径
三、反射
Java反射机制就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。这种动态获取类的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
- 类的构成
构造方法:Constructor
成员变量:Field
成员方法:Method
1.获取类的字节码文件对象
//获取一个类的字节码文件对象
public static void main(String[] args) throws ClassNotFoundException {
//方式1:通过Object类中的getClass方法
Student student = new Student();
Class<? extends Student> aClass = student.getClass();//class类的实例表示正在运行的Java应用程序中的类和接口
//方式2:任何一个类,都有一个class属性,通过这个属性就可以获取到该类的字节码文件对象
Class<Student> studentClass = Student.class;
//方式3:Class类的forname()方法
Class<?> aClass1 = Class.forName("demo1.Student");
//全路径:一个类带有包名 全类名:带有包名的类
}
2.通过反射获取构造方法
public static void main(String[] args) throws Exception {
Class<?> aClass = Class.forName("demo1.Student");//获取该类的字节码文件对象
Constructor<?>[] constructors = aClass.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}//获取该类的所有公共的构造方法
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}//获取该类所有的公共的、私有的构造方法
//获取该类的单个公共的构造方法
Constructor<?> constructor = aClass.getConstructor();
Constructor<?> constructor1 = aClass.getConstructor(String.class, int.class);
//获取该类的单个私有的构造方法
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, char.class);
Constructor<?> declaredConstructor1 = aClass.getDeclaredConstructor(int.class);
}
3.通过反射的方式创建对象
当构造方法是公共的时:
public static void main(String[] args) throws Exception {
Class<?> aClass = Class.forName("demo1.Student");
Constructor<?> constructor = aClass.getConstructor();//获取空参的构造方法对象
//通过构造方法中的newInstance()方法,创建该类对象
Object obj = constructor.newInstance();
System.out.println(obj);
Constructor<?> constructor1 = aClass.getConstructor(String.class, int.class);//通过有参构造来创建该类对象
Object o = constructor1.newInstance("zhangsan", 23);
System.out.println(o);
}
当构造方法是私有的时:
public static void main(String[] args) throws Exception {
Class<?> aClass = Class.forName("demo1.Teacher");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();//获取私有空参构造的对象
declaredConstructor.setAccessible(true);//取消权限检查
Object o = declaredConstructor.newInstance();
System.out.println(o);
Constructor<?> declaredConstructor1 = aClass.getDeclaredConstructor(String.class, int.class);//获取私有有参构造的对象
declaredConstructor1.setAccessible(true);//取消权限检查
Object o1 = declaredConstructor1.newInstance("lisi", 24);
System.out.println(o1);
}
空参构造创建对象的两种方式:
//如果只想用空参构造,创建一个类的对象,那么反射方式有2种
public static void main(String[] args) throws Exception {
Class<?> aClass = Class.forName("demo1.Cat");
//方式1
Constructor<?> constructor = aClass.getConstructor();
Object o = constructor.newInstance();
//方式2
Object o1 = aClass.newInstance();
}
4.通过反射获取成员变量
public static void main(String[] args) throws Exception {
Class<?> aClass = Class.forName("demo4.Student");
//获取所有的公共字段对象
Field[] fields = aClass.getFields();
for (Field field : fields) {
System.out.println(field);
}
//获取所有的字段对象,包括私有的
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
//获取单个公共字段对象
Field name = aClass.getField("name");
Field age = aClass.getField("age");
//获取单个私有字段对象
Field money = aClass.getDeclaredField("money");
}
5.给成员变量赋值
public static void main(String[] args) throws Exception {
Class<?> aClass = Class.forName("demo4.Student");
Constructor<?> constructor = aClass.getConstructor();
Object obj = constructor.newInstance();
Field name = aClass.getField("name");
name.set(obj,"张三");
Object o = name.get(obj);
System.out.println(o);
Field age = aClass.getField("age");
age.set(obj,23);
Object o1 = age.get(obj);
System.out.println(o1);
Field money = aClass.getDeclaredField("money");
money.setAccessible(true);//给私有字段设置值,取消权限检查
money.set(obj,200.0);
Object o2 = money.get(obj);
System.out.println(o2);
}
6.通过反射获取成员方法
public class Student {
private Student() {
}
public void show(){
System.out.println("空参数的show方法执行了");
}
public void test(String name,int age){
System.out.println("两个参数的test方法执行了"+name+age);
}
public String hehe(double num){
System.out.println("有返回值的方法执行了"+num);
return "呵呵";
}
private String haha(){
System.out.println("私有的方法执行了");
return "哈哈";
}
}
public static void main(String[] args) throws Exception {
Class<?> aClass = Class.forName("demo4.Student");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Student student = (Student) declaredConstructor.newInstance();
//获取所有的公共的方法对象,包括它的父类的公共方法
Method[] methods = aClass.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("..............");
//获取所有的公共的、私有的方法对象
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
System.out.println("................");
//获取单个公共方法对象
Method show = aClass.getMethod("show");
System.out.println(show);
//参数1:方法名 参数2:该方法形参类型的class类型
Method test = aClass.getMethod("test", String.class, int.class);
System.out.println(test);
Method hehe = aClass.getMethod("hehe", double.class);
System.out.println(hehe);
//获取单个私有方法对象
Method haha = aClass.getDeclaredMethod("haha");
System.out.println(haha);
}
7.通过反射,调方法
public static void main(String[] args) throws Exception {
Class<?> aClass = Class.forName("demo4.Student");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Student obj = (Student) declaredConstructor.newInstance();
//获取方法对象
Method show = aClass.getMethod("show");
//通过反射的方法,来让invoke()方法执行
show.invoke(obj);
Method test = aClass.getMethod("test", String.class, int.class);
//参数1:该类的对象 参数2:传给方法的形参要的值
test.invoke(obj,"zhangsan",23);
Method hehe = aClass.getMethod("hehe", double.class);
Object invoke = hehe.invoke(obj,3.14);
System.out.println(invoke);
//通过反射,调私有的方法
Method haha = aClass.getDeclaredMethod("haha");
haha.setAccessible(true);//调用私有方法,取消权限检查
Object invoke1 = haha.invoke(obj);
System.out.println(invoke1);
}
三、案例演示
1.通过反射运行配置文件内容
public class Cat {
public void eat(){
System.out.println("猫吃鱼");
}
public void sleep(){
System.out.println("猫白天睡觉");
}
}
public class Dog {
public void eat(){
System.out.println("狗吃骨头");
}
public void sleep(){
System.out.println("狗睡觉");
}
}
配置文件
className=demo4.Cat
methodName=eat
public static void main(String[] args) throws Exception {
//读取配置文件
Properties properties = new Properties();
properties.load(new FileReader("properties.txt"));
Class<?> className = Class.forName(properties.getProperty("className"));
Constructor<?> declaredConstructor = className.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Object obj = declaredConstructor.newInstance();
Method methodName = className.getDeclaredMethod(properties.getProperty("methodName"));
methodName.setAccessible(true);
methodName.invoke(obj);
}
2.通过反射越过泛型检查
public static void main(String[] args) throws Exception {
//我给你ArrayList<Integer> 的一个对象,我想在这个集合中添加一个字符串数据
ArrayList<Integer> list = new ArrayList<>();
list.add(100);
//泛型只在编译期有效,在运行期就擦除了
Class<? extends ArrayList> aClass = list.getClass();
//获取add方法对象
Method add = aClass.getDeclaredMethod("add", Object.class);
//让add方法执行
add.invoke(list,"abc");
System.out.println(list);
}
3.通过反射写一个通用的设置某个对象的某个属性为指定的值
public class Student {
private String name;
private int age;
private double money;
}
public class MyUtils {
private MyUtils() {
}
public static void setProperty(Object obj, String propertyName, Object value) throws NoSuchFieldException, IllegalAccessException {
Class<?> aClass = obj.getClass();
Field field = aClass.getDeclaredField(propertyName);
field.setAccessible(true);
field.set(obj,value);
}
//获取字段的值
public static Object getProperty(Object obj,String propertyName) throws NoSuchFieldException, IllegalAccessException {
Class<?> aClass = obj.getClass();
Field field = aClass.getDeclaredField(propertyName);
field.setAccessible(true);
Object objField = field.get(obj);
return objField;
}
}
public class MyTest1 {
public static void main(String[] args) throws Exception{
Student student = new Student();
MyUtils.setProperty(student,"name","张三");
MyUtils.setProperty(student,"age",34);
MyUtils.setProperty(student,"money",230.0);
Object name = MyUtils.getProperty(student, "name");
Object age = MyUtils.getProperty(student, "age");
Object money = MyUtils.getProperty(student, "money");
System.out.println(name);
System.out.println(age);
System.out.println(money);
}
}
四、动态代理
代理:本来该自己做的事情,却请了别人来做,被请的人就是代理对象。动态代理就是通过反射来生成一个代理。
目的:就是在不修改类的情况下,对类中的方法进行增强的一种手段。
UserDao被代理对象(目标对象)
代理对象(运行期生成代理对象)
//Proxy提供用于创建动态代理类和实例的静态方法
//static Object newProxyInstance (ClassLoader loader, Class < ?>[]interfaces, InvocationHandler h)返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序
JDK的动态代理,要求有接口
如果没有接口,使用cglib可以处理
public interface UserDao {
void insert();
void delete();
void query();
void update();
}
public class UserDaoImpl implements UserDao {
@Override
public void insert() {
System.out.println("增加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void query() {
System.out.println("查找了一个用户");
}
@Override
public void update() {
System.out.println("修改了一个用户");
}
}
public class ProxyUtils {
//返回一个代理对象
public static UserDao getProxy(UserDao userDao){
//创建一个代理对象
//static Object newProxyInstance (ClassLoader loader, Class < ?>[]interfaces, InvocationHandler h)
//返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
UserDao o = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), new InvocationHandler() {
/**
* 1.loader类加载器,负责加载代理对象的字节码文件,跟被代理对象是同一个类加载器,固定写法
* 2.接口对应的一个class数组,固定写法
* 3.InvocationHandler:用于提供增强的代码
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* 1.proxy代理对象
* 2.method方法对象
* 3.args方法上的参数数组
*/
Object invoke=null;
System.out.println("权限检查");
if(method.getName().equals("insert")){
invoke = method.invoke(userDao);
}
System.out.println("修改日志");
return invoke;
}
});
return o;
}
}
public static void main(String[] args) {
UserDaoImpl userDao = new UserDaoImpl();
UserDao proxy = ProxyUtils.getProxy(userDao);
proxy.insert();
}
五、枚举
- JDK1.5的新特性:自动拆装箱,泛型,增强for循环,可变参数,枚举
- 枚举:就是一个类只能存在几个固定的对象,那么这个就是枚举。
1.通过enum实现枚举类
public enum Direction {//枚举类
FRONT,BACK,LEFT,RIGHT;
//枚举项,必须位于第一行 最后一个枚举项的分号,如果下面没代码,就不用写,有就必须写上
private Direction() {
}//枚举的构造方法必须私有
}
public class MyTest {
public static void main(String[] args) {
Direction front = Direction.FRONT;
switch(front){
case FRONT:
System.out.println("前");
break;
case BACK:
System.out.println("后");
break;
case LEFT:
System.out.println("左");
break;
case RIGHT:
System.out.println("右");
break;
}
}
}
public enum Direction {//枚举类
FRONT("前"),BACK("后"),LEFT("左"),RIGHT("右");
private Direction(String name) {}
}
public class MyTest {
public static void main(String[] args) {
Direction front = Direction.FRONT;
Direction back = Direction.BACK;
Direction left = Direction.LEFT;
Direction right = Direction.RIGHT;
System.out.println(front);
System.out.println(back);
System.out.println(left);
System.out.println(right);
}
public enum Direction {//枚举类
FRONT(){
@Override
public void show(String name) {
System.out.println(name);
}
},BACK(){
@Override
public void show(String name) {
System.out.println(name);
}
},LEFT(){
@Override
public void show(String name) {
System.out.println(name);
}
},RIGHT(){
@Override
public void show(String name) {
System.out.println(name);
}
};
public abstract void show(String name);
private Direction() {
}
}
public class MyTest {
public static void main(String[] args) {
Direction front = Direction.FRONT;
Direction back = Direction.BACK;
Direction left = Direction.LEFT;
Direction right = Direction.RIGHT;
front.show("前");
back.show("后");
left.show("左");
right.show("右");
}
}