day27-Java反射/设计模式

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派生的类型代替
反射的三个阶段.png

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,动态代理 : 在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。
  • 总体来说设计模式分为三大类:
  • 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
  • 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
  • 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
    其实还有两类:并发型模式和线程池模式。用一个图片来整体描述一下:
设计模式1.png
设计模式2.png

END。
我是小侯爷。
在魔都艰苦奋斗,白天是上班族,晚上是知识服务工作者。
如果读完觉得有收获的话,记得关注和点赞哦。
非要打赏的话,我也是不会拒绝的。

推荐阅读更多精彩内容