27.01_类的加载概述和加载时机
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
-
加载
- 就是指将
class
文件读入内存,并为之创建一个Class
对象。任何类被使用时系统都会建立一个Class
对象。
- 就是指将
-
连接
- 1.验证 是否有正确的内部结构,并和其他类协调一致
- 2.准备 负责为类的静态成员分配内存,并设置默认初始化值
- 3.解析 将类的二进制数据中的符号引用替换为直接引用
初始化 就是我们以前讲过的初始化步骤
B:加载时机
* 创建类的实例
* 访问类的静态变量,或者为静态变量赋值
* 调用类的静态方法
* 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
* 初始化某个类的子类
* 直接使用java.exe命令来运行某个主类
27.02_类加载器的概述和分类
- A:类加载器的概述 : 负责将
.class
文件加载到内存中,并为之生成对应的Class
对象。虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。 - B:类加载器的分类
-
Bootstrap ClassLoader
根类加载器 -
Extension ClassLoader
扩展类加载器 -
Sysetm ClassLoader
系统类加载器
-
- C:类加载器的作用
- Bootstrap ClassLoader 根类加载器
- 也被称为引导类加载器,负责Java核心类的加载
- 比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
- Extension ClassLoader 扩展类加载器
- 负责JRE的扩展目录中jar包的加载。
- 在JDK中JRE的lib目录下ext目录
- Sysetm ClassLoader 系统类加载器
- 负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径
27.03_反射概述
- 1.1JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
- 1.2对于任意一个对象,都能够调用它的任意一个方法和属性;
- 1.3这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
- 1.4要想解剖一个类,必须先要获取到该类的字节码文件对象。
- 1.5而解剖使用的就是
Class
类中的方法,所以先要获取到每一个字节码文件对应的Class
类型的对象。 - 推荐2篇详细讲解Java反射的优秀文章:
Java基础与提高干货系列——Java反射机制
Java反射详解
Class clazz1 = Class.forName("com.bean.Person");
Class<?> clazz2 = Person.class;
Class<?> clazz3 = new Person().getClass(); //加不加<?>都一样,
System.out.println(clazz1 == clazz2);
Class<?> 相当于 Class<? extends Object>
?是个通配符,可以用任何由Object派生的类型代替
27.04_反射(Class.forName()读取配置文件举例)
// 反射和配置文件
BufferedReader br = new BufferedReader(new FileReader("config.plist"));
Class<?> clazz = Class.forName(br.readLine()); //从文件中读取了类名(全类名)
br.close();
Juicer j = new Juicer();
j.run( (Apple)clazz.newInstance() ); //使用 Class 创建了对象
--------------------分割线-----------------------------
class Juicer {
public void run(Apple a) { a.squeeze(); }
}
class Apple {
public void squeeze() { System.out.println("炸出一杯苹果汁"); }
}
27.05_通过反射获取带参构造方法并使用
- 关键类:
Constructor
构造方法类
Class
类的newInstance()
方法是使用该类无参的构造函数创建对象, 如果一个类没有无参的构造函数, 就不能这样创建了,可以调用Class
类的getConstructor(String.class,int.class)
方法获取一个指定的构造函数然后再调用Constructor
类的newInstance("张三",20)
方法创建对象
Class<?> clazz = Class.forName("com.bean.Person");
Constructor<?> c = clazz.getConstructor(String.class,int.class); //获取class的构造(有参)方法类
Person p = (Person) c.newInstance("张六",23); //使用有参构造方法类创建对象
System.out.println(p);
System.out.println(clazz.getConstructors()); //获取所有构造方法
27.06_通过反射获取成员变量并使用
- 关键类:
Field
成员变量类
Class.getField(String)
方法可以获取类中的指定字段(可见的), 如果是私有的可以用getDeclaedField("name")
方法获取,通过set(obj, "李四")
方法可以设置指定对象上该字段的值, 如果是私有的需要先调用setAccessible(true)
设置访问权限,用获取的指定的字段调用get(obj)
可以获取指定对象中该字段的值
// 通过反射,获取 成员变量
Class<?> clazz = Class.forName("com.bean.Person");
Constructor<?> c = clazz.getConstructor(String.class,int.class); //获取class的构造(有参)方法类
Person p = (Person) c.newInstance("张六",23); //使用有参构造方法类创建对象
//Field f = clazz.getField("name"); //获取name成员属性 只能获取public的成员属性
Field f = clazz.getDeclaredField("name"); //暴力反射,获取私有字段(成员属性/变量)
f.setAccessible(true); //去除私有权限,必须这样只会,才能修改值
f.set(p, "我日,这也可以"); //修改字段的值
System.out.println(p);
27.07_通过反射获取方法并使用
- 关键类:
Method
成员方法类
Class.getMethod(String, Class...)
和Class.getDeclaredMethod(String, Class...)
方法可以获取类中的指定方法,调用invoke(Object, Object...)
可以调用该方法,比如Class.getMethod("eat") invoke(obj)
和Class.getMethod("eat",int.class) invoke(obj,10)
Class<?> clazz = Class.forName("com.bean.Person");
Constructor<?> c = clazz.getConstructor(String.class , int.class);
Person p = (Person) c.newInstance("大叔",40);
Method m = clazz.getMethod("eat"); //获取eat方法,没有参数
m.invoke(p); //运行获取的eat方法,没有参数
Method m3 = clazz.getMethod("eat", int.class); //获取eat方法,带参数的
m3.invoke(p, 12); //运行获取的eat方法。带参数。
Method m2 = clazz.getDeclaredMethod("sleep"); //获取私有方法,没有参数
m2.setAccessible(true); //想要运行私有方法,必须先让方法可见
m2.invoke(p); //运行获取的私有方法,没有参数
27.08_通过反射越过泛型检查)
-
ArrayList<Integer>
的一个对象,在这个集合中添加一个字符串数据,如何实现呢?
// 1.不通过反射的办法, 泛型擦除,泛型只是在编译期做语法检查的,运行期泛型会被擦除掉
ArrayList list = null;
ArrayList<Integer> intList = new ArrayList<>();
intList.add(100);
list = intList; //改变list的内存指向
list.add("XXOO...我是字符串..."); //实际此时已经把字符串 添加到 ArrayList<Integer> 里面了
System.out.println(intList);
// 2.通过 反射
ArrayList<Integer> intList = new ArrayList<>();
intList.add(100);
Class<?> clazz = Class.forName("java.util.ArrayList");
Method m = clazz.getMethod("add", Object.class); //获取add方法
m.invoke(intList, "我是字符串,我进入int泛型集合了!");
System.out.println(intList);
27.09_通过反射写一个通用的设置某个对象的某个属性为指定的值
/**
* 把某个对象的属性,改成自己想要的值
* @param obj 对象
* @param k 对象的属性名称,字符串
* @param v 要改成的值
*/
public static void setPropertyByName(Object obj, String k, Object v) throws Exception {
Class<?> clazz = obj.getClass();
Field f = clazz.getDeclaredField(k); //不管私有方法还是public的,都能获取 暴力反射
f.setAccessible(true); //去除权限
f.set(obj, v);
}
27.11_动态代理的概述和实现
- 动态代理:在程序运行过程中产生的这个对象,而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理。
- 在Java中
java.lang.reflect
包下提供了一个Proxy
类和一个InvocationHandler
接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。我们有更强大的代理cglib
(JavaEE讲),Proxy
类中的方法创建动态代理类对象.
interface Animal { void eat(); }
class Dog implements Animal {
public void eat() {
System.out.println("狗改不了吃屎!");
}
}
---------------分割线---------------
class MyInvocationHandler implements InvocationHandler { //自定义代理功能类
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("先做一些事情...");
method.invoke(target, args); //执行被代理target对象的方法,多个参数本的本质是数组
System.out.println("后做一些事情...");
return null;
}
}
---------------分割线---------------
Dog dog = new Dog();
MyInvocationHandler delegate = new MyInvocationHandler(dog);
ClassLoader l = dog.getClass().getClassLoader(); //固定写法
Class<?>[] inter = dog.getClass().getInterfaces(); //固定写法
Animal animal = (Animal) Proxy.newProxyInstance(l, inter, delegate);
animal.eat(); //等于是让代理类,在方法运行时 多处理了一些事情。
27.12_设计模式(模版(Template)设计模式概述和使用)
模版方法模式就是定义一个算法的骨架,而将具体的算法延迟到子类中来实现
- a:优点 : 使用模版方法模式,在定义算法骨架的同时,可以很灵活的实现具体的算法,满足用户灵活多变的需求
- b:缺点 : 如果算法骨架有修改的话,则需要修改抽象类
// 一个抽象类,目的是计算一段代码运行的耗时
// 但是运行什么代码,是不知道的,需要子类去实现具体的代码
abstract class GetTime {
public final void getTime() { // final目的是不让子类重写
long s = System.currentTimeMillis();
code();
long e = System.currentTimeMillis();
System.out.println(e - s);
}
public abstract void code() ; // abstract必须让子类重写
}
27.14_设计模式
- 目前已学已讲的设计模式:
1,装饰 : 在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。典型就是JavaIO框架。
2,单例 : 一个类有且仅有一个实例,并且自行实例化向整个系统提供。
3,简单工厂 : 专门生产势力的类,把类的实例过程抽取到一个专门的类里。
4,工厂方法 : 创建一个工厂接口和创建多个工厂实现类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了
5,适配器 : 将一个类的接口转换成客户希望的另外一个接口。典型就是GUI里各种事件的处理。
6,模版 : 定义一个操作中的算法的骨架,而将步骤延迟到子类中。
7,动态代理 : 在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。
- 总体来说设计模式分为三大类:
- 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
- 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
-
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
其实还有两类:并发型模式和线程池模式。用一个图片来整体描述一下:
END。
我是小侯爷。
在魔都艰苦奋斗,白天是上班族,晚上是知识服务工作者。
如果读完觉得有收获的话,记得关注和点赞哦。
非要打赏的话,我也是不会拒绝的。